/* * This file is modified by Ivan Maidanski <ivmai@ivmaisoft.com> * Project name: JCGO-SUNAWT (http://www.ivmaisoft.com/jcgo/) */ /* * @(#)UnixPrintServiceLookup.java 1.13 03/01/23 * * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package sun.print; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.util.ArrayList; import java.util.Vector; import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import javax.print.DocFlavor; import javax.print.MultiDocPrintService; import javax.print.PrintService; import javax.print.PrintServiceLookup; import javax.print.attribute.Attribute; import javax.print.attribute.AttributeSet; import javax.print.attribute.HashPrintRequestAttributeSet; import javax.print.attribute.HashPrintServiceAttributeSet; import javax.print.attribute.PrintRequestAttribute; import javax.print.attribute.PrintRequestAttributeSet; import javax.print.attribute.PrintServiceAttribute; import javax.print.attribute.PrintServiceAttributeSet; import javax.print.attribute.standard.PrinterName; import java.io.File; import java.io.FileReader; /* * Remind: This class uses solaris commands. We also need a linux * version */ public class UnixPrintServiceLookup extends PrintServiceLookup implements BackgroundServiceLookup, Runnable { /* Remind: the current implementation is static, as its assumed * its preferable to minimise creation of PrintService instances. * Later we should add logic to add/remove services on the fly which * will take a hit of needing to regather the list of services. */ private String defaultPrinter; private PrintService defaultPrintService; private String[] printers; /* excludes the default printer */ private PrintService[] printServices; /* includes the default printer */ private Vector lookupListeners = null; static final String osname = (String)java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction("os.name")); static boolean isSysV() { return osname.indexOf("SunOS") != -1 || osname.indexOf("Solaris") != -1; } static boolean isBSD() { return osname.indexOf("Linux") != -1 || osname.equals("FreeBSD") || osname.equals("NetBSD") || osname.equals("OpenBSD") || osname.equals("DragonFly") || osname.equals("BSD/OS"); } static final int UNINITIALIZED = -1; static final int BSD_LPD = 0; static final int BSD_LPD_NG = 1; static int cmdIndex = UNINITIALIZED; String[] lpcFirstCom = { "/usr/sbin/lpc status | grep : | sed -ne '1,1 s/://p'", "/usr/sbin/lpc status | grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}'" }; String[] lpcAllCom = { "/usr/sbin/lpc status | grep : | sed -e 's/://'", "/usr/sbin/lpc -a status | grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}'" }; String[] lpcNameCom = { "| grep : | sed -ne 's/://p'", "| grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}'" }; static int getBSDCommandIndex() { String command = "/usr/sbin/lpc status | grep @"; String[] names = execCmd(command); if ((names == null) || (names.length == 0)) { return BSD_LPD; } else { return BSD_LPD_NG; } } /* Want the PrintService which is default print service to have * equality of reference with the equivalent in list of print services * This isn't required by the API and there's a risk doing this will * lead people to assume its guaranteed. */ public synchronized PrintService[] getPrintServices() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPrintJobAccess(); } if (printServices == null) { getDefaultPrintService(); if (isSysV()) { printers = getAllPrinterNamesSysV(); printServices = new PrintService[printers.length+1]; printServices[0] = getDefaultPrintService(); for (int p=0;p<printers.length; p++) { printServices[p+1] = new UnixPrintService(printers[p]); } } else { printers = getAllPrinterNamesBSD(); printServices = new PrintService[printers.length]; for (int p=0;p<printers.length; p++) { printServices[p] = new UnixPrintService(printers[p]); } } } return printServices; } private boolean matchesAttributes(PrintService service, PrintServiceAttributeSet attributes) { Attribute [] attrs = attributes.toArray(); Attribute serviceAttr; for (int i=0; i<attrs.length; i++) { serviceAttr = service.getAttribute(attrs[i].getCategory()); if (serviceAttr == null || !serviceAttr.equals(attrs[i])) { return false; } } return true; } /* This checks for validity of the printer name before passing as * parameter to a shell command. */ private boolean checkPrinterName(String s) { char c; for (int i=0; i < s.length(); i++) { c = s.charAt(i); if (Character.isLetterOrDigit(c) || c == '-' || c == '_' || c == '.' || c == '/') { continue; } else { return false; } } return true; } /* On a network with many (hundreds) of network printers, it * can save several seconds if you know all you want is a particular * printer, to ask for that printer rather than retrieving all printers. */ private PrintService getServiceByName(PrinterName nameAttr) { String name = nameAttr.getValue(); PrintService printer = null; if (name == null || name.equals("") || !checkPrinterName(name)) { return null; } if (isSysV()) { printer = getNamedPrinterNameSysV(name); } else { printer = getNamedPrinterNameBSD(name); } return printer; } private PrintService[] getPrintServices(PrintServiceAttributeSet serviceSet) { if (serviceSet == null || serviceSet.isEmpty()) { return getPrintServices(); } /* Typically expect that if a service attribute is specified that * its a printer name and there ought to be only one match. * Directly retrieve that service and confirm * that it meets the other requirements. * If printer name isn't mentioned then go a slow path checking * all printers if they meet the reqiremements. */ PrintService[] services; PrinterName name = (PrinterName)serviceSet.get(PrinterName.class); if (name != null) { /* To avoid execing a unix command see if the client is asking * for the default printer by name, since we already have that * initialised. */ PrintService defService = getDefaultPrintService(); PrinterName defName = (PrinterName)defService.getAttribute(PrinterName.class); if (defName != null && name.equals(defName)) { if (matchesAttributes(defService, serviceSet)) { services = new PrintService[1]; services[0] = defService; return services; } else { return new PrintService[0]; } } else { /* Its not the default service */ PrintService service = getServiceByName(name); if (service != null && matchesAttributes(service, serviceSet)) { services = new PrintService[1]; services[0] = service; return services; } else { return new PrintService[0]; } } } else { /* specified service attributes don't include a name.*/ Vector matchedServices = new Vector(); services = getPrintServices(); for (int i = 0; i< services.length; i++) { if (matchesAttributes(services[i], serviceSet)) { matchedServices.add(services[i]); } } services = new PrintService[matchedServices.size()]; for (int i = 0; i< services.length; i++) { services[i] = (PrintService)matchedServices.elementAt(i); } return services; } } /* * We know that all of these services are the same, so ask * only one and return none or all based on that result. * If service attributes are specified then there must be additional * filtering. */ public PrintService[] getPrintServices(DocFlavor flavor, AttributeSet attributes) { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPrintJobAccess(); } PrintRequestAttributeSet requestSet = null; PrintServiceAttributeSet serviceSet = null; if (attributes != null && !attributes.isEmpty()) { requestSet = new HashPrintRequestAttributeSet(); serviceSet = new HashPrintServiceAttributeSet(); Attribute[] attrs = attributes.toArray(); for (int i=0; i<attrs.length; i++) { if (attrs[i] instanceof PrintRequestAttribute) { requestSet.add(attrs[i]); } else if (attrs[i] instanceof PrintServiceAttribute) { serviceSet.add(attrs[i]); } } } PrintService service = getDefaultPrintService(); if ((flavor == null || service.isDocFlavorSupported(flavor)) && service.getUnsupportedAttributes(flavor, requestSet) == null) { return getPrintServices(serviceSet); } else { return new PrintService[0]; } } /* * return empty array as don't support multi docs */ public MultiDocPrintService[] getMultiDocPrintServices(DocFlavor[] flavors, AttributeSet attributes) { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPrintJobAccess(); } return new MultiDocPrintService[0]; } public synchronized PrintService getDefaultPrintService() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPrintJobAccess(); } if (defaultPrintService == null) { if (isSysV()) { defaultPrinter = getDefaultPrinterNameSysV(); } else { defaultPrinter = getDefaultPrinterNameBSD(); } defaultPrintService = new UnixPrintService(defaultPrinter); } return defaultPrintService; } public synchronized void getServicesInbackground(BackgroundLookupListener listener) { if (printServices != null) { listener.notifyServices(printServices); } else { if (lookupListeners == null) { lookupListeners = new Vector(); lookupListeners.add(listener); Thread lookupThread = new Thread(this); lookupThread.start(); } else { lookupListeners.add(listener); } } } /* This method isn't used in most cases because we rely on code in * javax.print.PrintServiceLookup. This is needed just for the cases * where those interfaces are by-passed. */ private PrintService[] copyOf(PrintService[] inArr) { if (inArr == null || inArr.length == 0) { return inArr; } else { PrintService []outArr = new PrintService[inArr.length]; System.arraycopy(inArr, 0, outArr, 0, inArr.length); return outArr; } } public void run() { PrintService[] services = getPrintServices(); synchronized (this) { BackgroundLookupListener listener; for (int i=0; i<lookupListeners.size(); i++) { listener = (BackgroundLookupListener)lookupListeners.elementAt(i); listener.notifyServices(copyOf(services)); } lookupListeners = null; } } private String getDefaultPrinterNameBSD() { if (cmdIndex == UNINITIALIZED) { cmdIndex = getBSDCommandIndex(); } String[] names = execCmd(lpcFirstCom[cmdIndex]); if (names == null || names.length == 0) { return null; } if ((cmdIndex==BSD_LPD_NG) && (names[0].startsWith("missingprinter"))) { return null; } return names[0]; } private PrintService getNamedPrinterNameBSD(String name) { if (cmdIndex == UNINITIALIZED) { cmdIndex = getBSDCommandIndex(); } String command = "/usr/sbin/lpc status " + name + lpcNameCom[cmdIndex]; String[] result = execCmd(command); if (result == null || !(result[0].equals(name))) { return null; } return new UnixPrintService(name); } private String[] getAllPrinterNamesBSD() { if (cmdIndex == UNINITIALIZED) { cmdIndex = getBSDCommandIndex(); } String[] names = execCmd(lpcAllCom[cmdIndex]); if (names == null || names.length == 0) { return null; } return names; } private String getDefaultPrinterNameSysV() { String defaultPrinter = "lp"; String command = "/usr/bin/lpstat -d|/usr/bin/expand|/usr/bin/cut -f4 -d' '"; String [] names = execCmd(command); if (names == null || names.length == 0) { return defaultPrinter; } else { return names[0]; } } private PrintService getNamedPrinterNameSysV(String name) { String command = "/usr/bin/lpstat -v " + name; String []result = execCmd(command); if (result == null || result[0].indexOf("unknown printer") > 0) { return null; } else { return new UnixPrintService(name); } } private String[] getAllPrinterNamesSysV() { String defaultPrinter = "lp"; String command = "/usr/bin/lpstat -v|/usr/bin/expand|/usr/bin/cut -f3 -d' ' |/usr/bin/cut -f1 -d':'"; String [] names = execCmd(command); ArrayList printerNames = new ArrayList(); for (int i=0; i < names.length; i++) { if (!names[i].equals("_default") && !names[i].equals(defaultPrinter)) { printerNames.add(names[i]); } } return (String[])printerNames.toArray(new String[printerNames.size()]); } static String[] execCmd(String command) { ArrayList results = new ArrayList(); try { final String[] cmd = new String[3]; if (isSysV()) { cmd[0] = "/usr/bin/sh"; cmd[1] = "-c"; cmd[2] = "env LC_ALL=C " + command; } else { cmd[0] = "/bin/sh"; cmd[1] = "-c"; cmd[2] = command; } BufferedReader bufferedReader = (BufferedReader)AccessController.doPrivileged( new PrivilegedExceptionAction() { public Object run() throws IOException { File f = File.createTempFile("prn","xc"); try { cmd[2] = cmd[2]+">"+f.getAbsolutePath(); Process lpstat = Runtime.getRuntime().exec(cmd); try { lpstat.waitFor(); } catch (InterruptedException e) { } if (lpstat.exitValue() == 0) { FileReader reader = new FileReader(f); return new BufferedReader(reader); } } finally { f.delete(); } return null; } }); if (bufferedReader != null) { String line; while((line = bufferedReader.readLine()) != null) { results.add(line); } } } catch (IOException e) { } catch (PrivilegedActionException e) { } return (String[])results.toArray(new String[results.size()]); } }