/* * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ 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 javax.print.attribute.standard.PrinterURI; import java.io.File; import java.io.FileReader; import java.net.URL; import java.nio.file.Files; /* * 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 minimize 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 PrintService[] printServices; /* includes the default printer */ private Vector lookupListeners = null; private static String debugPrefix = "UnixPrintServiceLookup>> "; private static boolean pollServices = true; private static final int DEFAULT_MINREFRESH = 120; // 2 minutes private static int minRefreshTime = DEFAULT_MINREFRESH; static String osname; static { /* The system property "sun.java2d.print.polling" * can be used to force the printing code to poll or not poll * for PrintServices. */ String pollStr = java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction("sun.java2d.print.polling")); if (pollStr != null) { if (pollStr.equalsIgnoreCase("true")) { pollServices = true; } else if (pollStr.equalsIgnoreCase("false")) { pollServices = false; } } /* The system property "sun.java2d.print.minRefreshTime" * can be used to specify minimum refresh time (in seconds) * for polling PrintServices. The default is 120. */ String refreshTimeStr = java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction( "sun.java2d.print.minRefreshTime")); if (refreshTimeStr != null) { try { minRefreshTime = (new Integer(refreshTimeStr)).intValue(); } catch (NumberFormatException e) { } if (minRefreshTime < DEFAULT_MINREFRESH) { minRefreshTime = DEFAULT_MINREFRESH; } } osname = java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction("os.name")); } static boolean isMac() { return osname.startsWith("Mac"); } static boolean isSysV() { return osname.equals("SunOS"); } static boolean isLinux() { return (osname.equals("Linux")); } static boolean isBSD() { return (osname.equals("Linux") || osname.contains("OS X")); } 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 all | grep : | sed -e 's/://'", "/usr/sbin/lpc status all | grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}' | sort" }; 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 all"; String[] names = execCmd(command); if ((names == null) || (names.length == 0)) { return BSD_LPD_NG; } for (int i=0; i 0)) { printers = new String[printerURIs.length]; for (int i=0; i 0) { PrintService saveService = printServices[0]; printServices[0] = printServices[defaultIndex]; printServices[defaultIndex] = saveService; } } private boolean matchesAttributes(PrintService service, PrintServiceAttributeSet attributes) { Attribute [] attrs = attributes.toArray(); Attribute serviceAttr; for (int i=0; i)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; } /* * Gets the printer name compatible with the list of printers returned by * the system when we query default or all the available printers. */ private String getPrinterDestName(PrintService ps) { if (isMac()) { return ((IPPPrintService)ps).getDest(); } return ps.getName(); } /* 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(); if (name == null || name.equals("") || !checkPrinterName(name)) { return null; } /* check if all printers are already available */ if (printServices != null) { for (PrintService printService : printServices) { PrinterName printerName = (PrinterName)printService.getAttribute(PrinterName.class); if (printerName.getValue().equals(name)) { return printService; } } } /* take CUPS into account first */ if (CUPSPrinter.isCupsRunning()) { try { return new IPPPrintService(name, new URL("http://"+ CUPSPrinter.getServer()+":"+ CUPSPrinter.getPort()+"/"+ name)); } catch (Exception e) { IPPPrintService.debug_println(debugPrefix+ " getServiceByName Exception "+ e); } } /* fallback if nothing not having a printer at this point */ PrintService printer = null; if (isMac() || 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); PrintService defService; if (name != null && (defService = getDefaultPrintService()) != 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. */ 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; } } /* * 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 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':' | /usr/bin/sort"; 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) && !names[i].equals("")) { printerNames.add(names[i]); } } return (String[])printerNames.toArray(new String[printerNames.size()]); } static String[] execCmd(final String command) { ArrayList results = null; 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] = "LC_ALL=C " + command; } results = (ArrayList)AccessController.doPrivileged( new PrivilegedExceptionAction() { public Object run() throws IOException { Process proc; BufferedReader bufferedReader = null; File f = Files.createTempFile("prn","xc").toFile(); cmd[2] = cmd[2]+">"+f.getAbsolutePath(); proc = Runtime.getRuntime().exec(cmd); try { boolean done = false; // in case of interrupt. while (!done) { try { proc.waitFor(); done = true; } catch (InterruptedException e) { } } if (proc.exitValue() == 0) { FileReader reader = new FileReader(f); bufferedReader = new BufferedReader(reader); String line; ArrayList results = new ArrayList(); while ((line = bufferedReader.readLine()) != null) { results.add(line); } return results; } } finally { f.delete(); // promptly close all streams. if (bufferedReader != null) { bufferedReader.close(); } proc.getInputStream().close(); proc.getErrorStream().close(); proc.getOutputStream().close(); } return null; } }); } catch (PrivilegedActionException e) { } if (results == null) { return new String[0]; } else { return (String[])results.toArray(new String[results.size()]); } } private class PrinterChangeListener extends Thread { public void run() { int refreshSecs; while (true) { try { refreshServices(); } catch (Exception se) { IPPPrintService.debug_println(debugPrefix+"Exception in refresh thread."); break; } if ((printServices != null) && (printServices.length > minRefreshTime)) { // compute new refresh time 1 printer = 1 sec refreshSecs = printServices.length; } else { refreshSecs = minRefreshTime; } try { sleep(refreshSecs * 1000); } catch (InterruptedException e) { break; } } } } }