1 /*
   2  * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.print;
  27 
  28 import sun.misc.ManagedLocalsThread;
  29 
  30 import java.io.BufferedReader;
  31 import java.io.FileInputStream;
  32 import java.io.InputStream;
  33 import java.io.InputStreamReader;
  34 import java.io.IOException;
  35 import java.util.ArrayList;
  36 import java.util.Vector;
  37 import java.security.AccessController;
  38 import java.security.PrivilegedActionException;
  39 import java.security.PrivilegedExceptionAction;
  40 import javax.print.DocFlavor;
  41 import javax.print.MultiDocPrintService;
  42 import javax.print.PrintService;
  43 import javax.print.PrintServiceLookup;
  44 import javax.print.attribute.Attribute;
  45 import javax.print.attribute.AttributeSet;
  46 import javax.print.attribute.HashPrintRequestAttributeSet;
  47 import javax.print.attribute.HashPrintServiceAttributeSet;
  48 import javax.print.attribute.PrintRequestAttribute;
  49 import javax.print.attribute.PrintRequestAttributeSet;
  50 import javax.print.attribute.PrintServiceAttribute;
  51 import javax.print.attribute.PrintServiceAttributeSet;
  52 import javax.print.attribute.standard.PrinterName;
  53 import javax.print.attribute.standard.PrinterURI;
  54 import java.io.File;
  55 import java.io.FileReader;
  56 import java.net.URL;
  57 import java.nio.file.Files;
  58 
  59 /*
  60  * Remind: This class uses solaris commands. We also need a linux
  61  * version
  62  */
  63 public class PrintServiceLookupProvider extends PrintServiceLookup
  64     implements BackgroundServiceLookup, Runnable {
  65 
  66     /* Remind: the current implementation is static, as its assumed
  67      * its preferable to minimize creation of PrintService instances.
  68      * Later we should add logic to add/remove services on the fly which
  69      * will take a hit of needing to regather the list of services.
  70      */
  71     private String defaultPrinter;
  72     private PrintService defaultPrintService;
  73     private PrintService[] printServices; /* includes the default printer */
  74     private Vector<BackgroundLookupListener> lookupListeners = null;
  75     private static String debugPrefix = "PrintServiceLookupProvider>> ";
  76     private static boolean pollServices = true;
  77     private static final int DEFAULT_MINREFRESH = 120;  // 2 minutes
  78     private static int minRefreshTime = DEFAULT_MINREFRESH;
  79 
  80 
  81     static String osname;
  82 
  83     // List of commands used to deal with the printer queues on AIX
  84     String[] lpNameComAix = {
  85       "/usr/bin/lsallq",
  86       "/usr/bin/lpstat -W -p|/usr/bin/expand|/usr/bin/cut -f1 -d' '",
  87       "/usr/bin/lpstat -W -d|/usr/bin/expand|/usr/bin/cut -f1 -d' '",
  88       "/usr/bin/lpstat -W -v"
  89     };
  90     private static final int aix_lsallq = 0;
  91     private static final int aix_lpstat_p = 1;
  92     private static final int aix_lpstat_d = 2;
  93     private static final int aix_lpstat_v = 3;
  94     private static int aix_defaultPrinterEnumeration = aix_lsallq;
  95 
  96     static {
  97         /* The system property "sun.java2d.print.polling"
  98          * can be used to force the printing code to poll or not poll
  99          * for PrintServices.
 100          */
 101         String pollStr = java.security.AccessController.doPrivileged(
 102             new sun.security.action.GetPropertyAction("sun.java2d.print.polling"));
 103 
 104         if (pollStr != null) {
 105             if (pollStr.equalsIgnoreCase("true")) {
 106                 pollServices = true;
 107             } else if (pollStr.equalsIgnoreCase("false")) {
 108                 pollServices = false;
 109             }
 110         }
 111 
 112         /* The system property "sun.java2d.print.minRefreshTime"
 113          * can be used to specify minimum refresh time (in seconds)
 114          * for polling PrintServices.  The default is 120.
 115          */
 116         String refreshTimeStr = java.security.AccessController.doPrivileged(
 117             new sun.security.action.GetPropertyAction(
 118                 "sun.java2d.print.minRefreshTime"));
 119 
 120         if (refreshTimeStr != null) {
 121             try {
 122                 minRefreshTime = (new Integer(refreshTimeStr)).intValue();
 123             } catch (NumberFormatException e) {
 124             }
 125             if (minRefreshTime < DEFAULT_MINREFRESH) {
 126                 minRefreshTime = DEFAULT_MINREFRESH;
 127             }
 128         }
 129 
 130         osname = java.security.AccessController.doPrivileged(
 131             new sun.security.action.GetPropertyAction("os.name"));
 132 
 133         /* The system property "sun.java2d.print.aix.lpstat"
 134          * can be used to force the usage of 'lpstat -p' to enumerate all
 135          * printer queues. By default we use 'lsallq', because 'lpstat -p' can
 136          * take lots of time if thousands of printers are attached to a server.
 137          */
 138         if (isAIX()) {
 139             String aixPrinterEnumerator = java.security.AccessController.doPrivileged(
 140                 new sun.security.action.GetPropertyAction("sun.java2d.print.aix.lpstat"));
 141 
 142             if (aixPrinterEnumerator != null) {
 143                 if (aixPrinterEnumerator.equalsIgnoreCase("lpstat")) {
 144                     aix_defaultPrinterEnumeration = aix_lpstat_p;
 145                 } else if (aixPrinterEnumerator.equalsIgnoreCase("lsallq")) {
 146                     aix_defaultPrinterEnumeration = aix_lsallq;
 147                 }
 148             }
 149         }
 150     }
 151 
 152     static boolean isMac() {
 153         return osname.startsWith("Mac");
 154     }
 155 
 156     static boolean isSysV() {
 157         return osname.equals("SunOS");
 158     }
 159 
 160     static boolean isLinux() {
 161         return (osname.equals("Linux"));
 162     }
 163 
 164     static boolean isBSD() {
 165         return (osname.equals("Linux") ||
 166                 osname.contains("OS X"));
 167     }
 168 
 169     static boolean isAIX() {
 170         return osname.equals("AIX");
 171     }
 172 
 173     static final int UNINITIALIZED = -1;
 174     static final int BSD_LPD = 0;
 175     static final int BSD_LPD_NG = 1;
 176 
 177     static int cmdIndex = UNINITIALIZED;
 178 
 179     String[] lpcFirstCom = {
 180         "/usr/sbin/lpc status | grep : | sed -ne '1,1 s/://p'",
 181         "/usr/sbin/lpc status | grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}'"
 182     };
 183 
 184     String[] lpcAllCom = {
 185         "/usr/sbin/lpc status all | grep : | sed -e 's/://'",
 186         "/usr/sbin/lpc status all | grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}' | sort"
 187     };
 188 
 189     String[] lpcNameCom = {
 190         "| grep : | sed -ne 's/://p'",
 191         "| grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}'"
 192     };
 193 
 194 
 195     static int getBSDCommandIndex() {
 196         String command  = "/usr/sbin/lpc status all";
 197         String[] names = execCmd(command);
 198 
 199         if ((names == null) || (names.length == 0)) {
 200             return BSD_LPD_NG;
 201         }
 202 
 203         for (int i=0; i<names.length; i++) {
 204             if (names[i].indexOf('@') != -1) {
 205                 return BSD_LPD_NG;
 206             }
 207         }
 208 
 209         return BSD_LPD;
 210     }
 211 
 212 
 213     public PrintServiceLookupProvider() {
 214         // start the printer listener thread
 215         if (pollServices) {
 216             Thread thr = new ManagedLocalsThread(new PrinterChangeListener());
 217             thr.setDaemon(true);
 218             thr.start();
 219             IPPPrintService.debug_println(debugPrefix+"polling turned on");
 220         }
 221     }
 222 
 223     /* Want the PrintService which is default print service to have
 224      * equality of reference with the equivalent in list of print services
 225      * This isn't required by the API and there's a risk doing this will
 226      * lead people to assume its guaranteed.
 227      */
 228     public synchronized PrintService[] getPrintServices() {
 229         SecurityManager security = System.getSecurityManager();
 230         if (security != null) {
 231             security.checkPrintJobAccess();
 232         }
 233 
 234         if (printServices == null || !pollServices) {
 235             refreshServices();
 236         }
 237         if (printServices == null) {
 238             return new PrintService[0];
 239         } else {
 240             return printServices.clone();
 241         }
 242     }
 243 
 244     private int addPrintServiceToList(ArrayList<PrintService> printerList, PrintService ps) {
 245         int index = printerList.indexOf(ps);
 246         // Check if PrintService with same name is already in the list.
 247         if (CUPSPrinter.isCupsRunning() && index != -1) {
 248             // Bug in Linux: Duplicate entry of a remote printer
 249             // and treats it as local printer but it is returning wrong
 250             // information when queried using IPP. Workaround is to remove it.
 251             // Even CUPS ignores these entries as shown in lpstat or using
 252             // their web configuration.
 253             PrinterURI uri = ps.getAttribute(PrinterURI.class);
 254             if (uri.getURI().getHost().equals("localhost")) {
 255                 IPPPrintService.debug_println(debugPrefix+"duplicate PrintService, ignoring the new local printer: "+ps);
 256                 return index;  // Do not add this.
 257             }
 258             PrintService oldPS = printerList.get(index);
 259             uri = oldPS.getAttribute(PrinterURI.class);
 260             if (uri.getURI().getHost().equals("localhost")) {
 261                 IPPPrintService.debug_println(debugPrefix+"duplicate PrintService, removing existing local printer: "+oldPS);
 262                 printerList.remove(oldPS);
 263             } else {
 264                 return index;
 265             }
 266         }
 267         printerList.add(ps);
 268         return (printerList.size() - 1);
 269     }
 270 
 271 
 272     // refreshes "printServices"
 273     public synchronized void refreshServices() {
 274         /* excludes the default printer */
 275         String[] printers = null; // array of printer names
 276         String[] printerURIs = null; //array of printer URIs
 277 
 278         try {
 279             getDefaultPrintService();
 280         } catch (Throwable t) {
 281             IPPPrintService.debug_println(debugPrefix+
 282               "Exception getting default printer : " + t);
 283         }
 284         if (CUPSPrinter.isCupsRunning()) {
 285             try {
 286                 printerURIs = CUPSPrinter.getAllPrinters();
 287                 IPPPrintService.debug_println("CUPS URIs = " + printerURIs);
 288                 if (printerURIs != null) {
 289                     for (int p = 0; p < printerURIs.length; p++) {
 290                        IPPPrintService.debug_println("URI="+printerURIs[p]);
 291                     }
 292                 }
 293             } catch (Throwable t) {
 294             IPPPrintService.debug_println(debugPrefix+
 295               "Exception getting all CUPS printers : " + t);
 296             }
 297             if ((printerURIs != null) && (printerURIs.length > 0)) {
 298                 printers = new String[printerURIs.length];
 299                 for (int i=0; i<printerURIs.length; i++) {
 300                     int lastIndex = printerURIs[i].lastIndexOf("/");
 301                     printers[i] = printerURIs[i].substring(lastIndex+1);
 302                 }
 303             }
 304         } else {
 305             if (isMac() || isSysV()) {
 306                 printers = getAllPrinterNamesSysV();
 307             } else if (isAIX()) {
 308                 printers = getAllPrinterNamesAIX();
 309             } else { //BSD
 310                 printers = getAllPrinterNamesBSD();
 311             }
 312         }
 313 
 314         if (printers == null) {
 315             if (defaultPrintService != null) {
 316                 printServices = new PrintService[1];
 317                 printServices[0] = defaultPrintService;
 318             } else {
 319                 printServices = null;
 320             }
 321             return;
 322         }
 323 
 324         ArrayList<PrintService> printerList = new ArrayList<>();
 325         int defaultIndex = -1;
 326         for (int p=0; p<printers.length; p++) {
 327             if (printers[p] == null) {
 328                 continue;
 329             }
 330             if ((defaultPrintService != null)
 331                 && printers[p].equals(getPrinterDestName(defaultPrintService))) {
 332                 defaultIndex = addPrintServiceToList(printerList, defaultPrintService);
 333             } else {
 334                 if (printServices == null) {
 335                     IPPPrintService.debug_println(debugPrefix+
 336                                                   "total# of printers = "+printers.length);
 337 
 338                     if (CUPSPrinter.isCupsRunning()) {
 339                         try {
 340                             addPrintServiceToList(printerList,
 341                                                   new IPPPrintService(printers[p],
 342                                                                    printerURIs[p],
 343                                                                    true));
 344                         } catch (Exception e) {
 345                             IPPPrintService.debug_println(debugPrefix+
 346                                                           " getAllPrinters Exception "+
 347                                                           e);
 348 
 349                         }
 350                     } else {
 351                         printerList.add(new UnixPrintService(printers[p]));
 352                     }
 353                 } else {
 354                     int j;
 355                     for (j=0; j<printServices.length; j++) {
 356                         if (printServices[j] != null) {
 357                             if (printers[p].equals(getPrinterDestName(printServices[j]))) {
 358                                 printerList.add(printServices[j]);
 359                                 printServices[j] = null;
 360                                 break;
 361                             }
 362                         }
 363                     }
 364 
 365                     if (j == printServices.length) {      // not found?
 366                         if (CUPSPrinter.isCupsRunning()) {
 367                             try {
 368                                 addPrintServiceToList(printerList,
 369                                              new IPPPrintService(printers[p],
 370                                                                  printerURIs[p],
 371                                                                  true));
 372                             } catch (Exception e) {
 373                                 IPPPrintService.debug_println(debugPrefix+
 374                                                               " getAllPrinters Exception "+
 375                                                               e);
 376 
 377                             }
 378                         } else {
 379                             printerList.add(new UnixPrintService(printers[p]));
 380                         }
 381                     }
 382                 }
 383             }
 384         }
 385 
 386         // Look for deleted services and invalidate these
 387         if (printServices != null) {
 388             for (int j=0; j < printServices.length; j++) {
 389                 if ((printServices[j] instanceof UnixPrintService) &&
 390                     (!printServices[j].equals(defaultPrintService))) {
 391                     ((UnixPrintService)printServices[j]).invalidateService();
 392                 }
 393             }
 394         }
 395 
 396         //if defaultService is not found in printerList
 397         if (defaultIndex == -1 && defaultPrintService != null) {
 398             defaultIndex = addPrintServiceToList(printerList, defaultPrintService);
 399         }
 400 
 401         printServices = printerList.toArray(new PrintService[] {});
 402 
 403         // swap default with the first in the list
 404         if (defaultIndex > 0) {
 405             PrintService saveService = printServices[0];
 406             printServices[0] = printServices[defaultIndex];
 407             printServices[defaultIndex] = saveService;
 408         }
 409     }
 410 
 411     private boolean matchesAttributes(PrintService service,
 412                                       PrintServiceAttributeSet attributes) {
 413 
 414         Attribute [] attrs =  attributes.toArray();
 415         for (int i=0; i<attrs.length; i++) {
 416             @SuppressWarnings("unchecked")
 417             Attribute serviceAttr
 418                 = service.getAttribute((Class<PrintServiceAttribute>)attrs[i].getCategory());
 419             if (serviceAttr == null || !serviceAttr.equals(attrs[i])) {
 420                 return false;
 421             }
 422         }
 423         return true;
 424     }
 425 
 426       /* This checks for validity of the printer name before passing as
 427        * parameter to a shell command.
 428        */
 429       private boolean checkPrinterName(String s) {
 430         char c;
 431 
 432         for (int i=0; i < s.length(); i++) {
 433           c = s.charAt(i);
 434           if (Character.isLetterOrDigit(c) ||
 435               c == '-' || c == '_' || c == '.' || c == '/') {
 436             continue;
 437           } else {
 438             return false;
 439           }
 440         }
 441         return true;
 442       }
 443 
 444     /*
 445      * Gets the printer name compatible with the list of printers returned by
 446      * the system when we query default or all the available printers.
 447      */
 448     private String getPrinterDestName(PrintService ps) {
 449         if (isMac()) {
 450             return ((IPPPrintService)ps).getDest();
 451         }
 452         return ps.getName();
 453     }
 454 
 455     /* On a network with many (hundreds) of network printers, it
 456      * can save several seconds if you know all you want is a particular
 457      * printer, to ask for that printer rather than retrieving all printers.
 458      */
 459     private PrintService getServiceByName(PrinterName nameAttr) {
 460         String name = nameAttr.getValue();
 461         if (name == null || name.equals("") || !checkPrinterName(name)) {
 462             return null;
 463         }
 464         /* check if all printers are already available */
 465         if (printServices != null) {
 466             for (PrintService printService : printServices) {
 467                 PrinterName printerName = printService.getAttribute(PrinterName.class);
 468                 if (printerName.getValue().equals(name)) {
 469                     return printService;
 470                 }
 471             }
 472         }
 473         /* take CUPS into account first */
 474         if (CUPSPrinter.isCupsRunning()) {
 475             try {
 476                 return new IPPPrintService(name,
 477                                            new URL("http://"+
 478                                                    CUPSPrinter.getServer()+":"+
 479                                                    CUPSPrinter.getPort()+"/"+
 480                                                    name));
 481             } catch (Exception e) {
 482                 IPPPrintService.debug_println(debugPrefix+
 483                                               " getServiceByName Exception "+
 484                                               e);
 485             }
 486         }
 487         /* fallback if nothing not having a printer at this point */
 488         PrintService printer = null;
 489         if (isMac() || isSysV()) {
 490             printer = getNamedPrinterNameSysV(name);
 491         } else if (isAIX()) {
 492             printer = getNamedPrinterNameAIX(name);
 493         } else {
 494             printer = getNamedPrinterNameBSD(name);
 495         }
 496         return printer;
 497     }
 498 
 499     private PrintService[]
 500         getPrintServices(PrintServiceAttributeSet serviceSet) {
 501 
 502         if (serviceSet == null || serviceSet.isEmpty()) {
 503             return getPrintServices();
 504         }
 505 
 506         /* Typically expect that if a service attribute is specified that
 507          * its a printer name and there ought to be only one match.
 508          * Directly retrieve that service and confirm
 509          * that it meets the other requirements.
 510          * If printer name isn't mentioned then go a slow path checking
 511          * all printers if they meet the reqiremements.
 512          */
 513         PrintService[] services;
 514         PrinterName name = (PrinterName)serviceSet.get(PrinterName.class);
 515         PrintService defService;
 516         if (name != null && (defService = getDefaultPrintService()) != null) {
 517             /* To avoid execing a unix command  see if the client is asking
 518              * for the default printer by name, since we already have that
 519              * initialised.
 520              */
 521 
 522             PrinterName defName = defService.getAttribute(PrinterName.class);
 523 
 524             if (defName != null && name.equals(defName)) {
 525                 if (matchesAttributes(defService, serviceSet)) {
 526                     services = new PrintService[1];
 527                     services[0] = defService;
 528                     return services;
 529                 } else {
 530                     return new PrintService[0];
 531                 }
 532             } else {
 533                 /* Its not the default service */
 534                 PrintService service = getServiceByName(name);
 535                 if (service != null &&
 536                     matchesAttributes(service, serviceSet)) {
 537                     services = new PrintService[1];
 538                     services[0] = service;
 539                     return services;
 540                 } else {
 541                     return new PrintService[0];
 542                 }
 543             }
 544         } else {
 545             /* specified service attributes don't include a name.*/
 546             Vector<PrintService> matchedServices = new Vector<>();
 547             services = getPrintServices();
 548             for (int i = 0; i< services.length; i++) {
 549                 if (matchesAttributes(services[i], serviceSet)) {
 550                     matchedServices.add(services[i]);
 551                 }
 552             }
 553             services = new PrintService[matchedServices.size()];
 554             for (int i = 0; i< services.length; i++) {
 555                 services[i] = matchedServices.elementAt(i);
 556             }
 557             return services;
 558         }
 559     }
 560 
 561     /*
 562      * If service attributes are specified then there must be additional
 563      * filtering.
 564      */
 565     public PrintService[] getPrintServices(DocFlavor flavor,
 566                                            AttributeSet attributes) {
 567         SecurityManager security = System.getSecurityManager();
 568         if (security != null) {
 569           security.checkPrintJobAccess();
 570         }
 571         PrintRequestAttributeSet requestSet = null;
 572         PrintServiceAttributeSet serviceSet = null;
 573 
 574         if (attributes != null && !attributes.isEmpty()) {
 575 
 576             requestSet = new HashPrintRequestAttributeSet();
 577             serviceSet = new HashPrintServiceAttributeSet();
 578 
 579             Attribute[] attrs = attributes.toArray();
 580             for (int i=0; i<attrs.length; i++) {
 581                 if (attrs[i] instanceof PrintRequestAttribute) {
 582                     requestSet.add(attrs[i]);
 583                 } else if (attrs[i] instanceof PrintServiceAttribute) {
 584                     serviceSet.add(attrs[i]);
 585                 }
 586             }
 587         }
 588 
 589         PrintService[] services = getPrintServices(serviceSet);
 590         if (services.length == 0) {
 591             return services;
 592         }
 593 
 594         if (CUPSPrinter.isCupsRunning()) {
 595             ArrayList<PrintService> matchingServices = new ArrayList<>();
 596             for (int i=0; i<services.length; i++) {
 597                 try {
 598                     if (services[i].
 599                         getUnsupportedAttributes(flavor, requestSet) == null) {
 600                         matchingServices.add(services[i]);
 601                     }
 602                 } catch (IllegalArgumentException e) {
 603                 }
 604             }
 605             services = new PrintService[matchingServices.size()];
 606             return matchingServices.toArray(services);
 607 
 608         } else {
 609             // We only need to compare 1 PrintService because all
 610             // UnixPrintServices are the same anyway.  We will not use
 611             // default PrintService because it might be null.
 612             PrintService service = services[0];
 613             if ((flavor == null ||
 614                  service.isDocFlavorSupported(flavor)) &&
 615                  service.getUnsupportedAttributes(flavor, requestSet) == null)
 616             {
 617                 return services;
 618             } else {
 619                 return new PrintService[0];
 620             }
 621         }
 622     }
 623 
 624     /*
 625      * return empty array as don't support multi docs
 626      */
 627     public MultiDocPrintService[]
 628         getMultiDocPrintServices(DocFlavor[] flavors,
 629                                  AttributeSet attributes) {
 630         SecurityManager security = System.getSecurityManager();
 631         if (security != null) {
 632           security.checkPrintJobAccess();
 633         }
 634         return new MultiDocPrintService[0];
 635     }
 636 
 637 
 638     public synchronized PrintService getDefaultPrintService() {
 639         SecurityManager security = System.getSecurityManager();
 640         if (security != null) {
 641           security.checkPrintJobAccess();
 642         }
 643 
 644         // clear defaultPrintService
 645         defaultPrintService = null;
 646         String psuri = null;
 647 
 648         IPPPrintService.debug_println("isRunning ? "+
 649                                       (CUPSPrinter.isCupsRunning()));
 650         if (CUPSPrinter.isCupsRunning()) {
 651             String[] printerInfo = CUPSPrinter.getDefaultPrinter();
 652             if (printerInfo != null && printerInfo.length >= 2) {
 653                 defaultPrinter = printerInfo[0];
 654                 psuri = printerInfo[1];
 655             }
 656         } else {
 657             if (isMac() || isSysV()) {
 658                 defaultPrinter = getDefaultPrinterNameSysV();
 659             } else if (isAIX()) {
 660                 defaultPrinter = getDefaultPrinterNameAIX();
 661             } else {
 662                 defaultPrinter = getDefaultPrinterNameBSD();
 663             }
 664         }
 665         if (defaultPrinter == null) {
 666             return null;
 667         }
 668         defaultPrintService = null;
 669         if (printServices != null) {
 670             for (int j=0; j<printServices.length; j++) {
 671                 if (defaultPrinter.equals(getPrinterDestName(printServices[j]))) {
 672                     defaultPrintService = printServices[j];
 673                     break;
 674                 }
 675             }
 676         }
 677         if (defaultPrintService == null) {
 678             if (CUPSPrinter.isCupsRunning()) {
 679                 try {
 680                     PrintService defaultPS;
 681                     if ((psuri != null) && !psuri.startsWith("file")) {
 682                         defaultPS = new IPPPrintService(defaultPrinter,
 683                                                         psuri, true);
 684                     } else {
 685                         defaultPS = new IPPPrintService(defaultPrinter,
 686                                             new URL("http://"+
 687                                                     CUPSPrinter.getServer()+":"+
 688                                                     CUPSPrinter.getPort()+"/"+
 689                                                     defaultPrinter));
 690                     }
 691                     defaultPrintService = defaultPS;
 692                 } catch (Exception e) {
 693                 }
 694             } else {
 695                 defaultPrintService = new UnixPrintService(defaultPrinter);
 696             }
 697         }
 698 
 699         return defaultPrintService;
 700     }
 701 
 702     public synchronized void
 703         getServicesInbackground(BackgroundLookupListener listener) {
 704         if (printServices != null) {
 705             listener.notifyServices(printServices);
 706         } else {
 707             if (lookupListeners == null) {
 708                 lookupListeners = new Vector<>();
 709                 lookupListeners.add(listener);
 710                 Thread lookupThread = new Thread(this);
 711                 lookupThread.start();
 712             } else {
 713                 lookupListeners.add(listener);
 714             }
 715         }
 716     }
 717 
 718     /* This method isn't used in most cases because we rely on code in
 719      * javax.print.PrintServiceLookup. This is needed just for the cases
 720      * where those interfaces are by-passed.
 721      */
 722     private PrintService[] copyOf(PrintService[] inArr) {
 723         if (inArr == null || inArr.length == 0) {
 724             return inArr;
 725         } else {
 726             PrintService []outArr = new PrintService[inArr.length];
 727             System.arraycopy(inArr, 0, outArr, 0, inArr.length);
 728             return outArr;
 729         }
 730     }
 731 
 732     public void run() {
 733         PrintService[] services = getPrintServices();
 734         synchronized (this) {
 735             BackgroundLookupListener listener;
 736             for (int i=0; i<lookupListeners.size(); i++) {
 737                 listener = lookupListeners.elementAt(i);
 738                 listener.notifyServices(copyOf(services));
 739             }
 740             lookupListeners = null;
 741         }
 742     }
 743 
 744     private String getDefaultPrinterNameBSD() {
 745         if (cmdIndex == UNINITIALIZED) {
 746             cmdIndex = getBSDCommandIndex();
 747         }
 748         String[] names = execCmd(lpcFirstCom[cmdIndex]);
 749         if (names == null || names.length == 0) {
 750             return null;
 751         }
 752 
 753         if ((cmdIndex==BSD_LPD_NG) &&
 754             (names[0].startsWith("missingprinter"))) {
 755             return null;
 756         }
 757         return names[0];
 758     }
 759 
 760     private PrintService getNamedPrinterNameBSD(String name) {
 761       if (cmdIndex == UNINITIALIZED) {
 762         cmdIndex = getBSDCommandIndex();
 763       }
 764       String command = "/usr/sbin/lpc status " + name + lpcNameCom[cmdIndex];
 765       String[] result = execCmd(command);
 766 
 767       if (result == null || !(result[0].equals(name))) {
 768           return null;
 769       }
 770       return new UnixPrintService(name);
 771     }
 772 
 773     private String[] getAllPrinterNamesBSD() {
 774         if (cmdIndex == UNINITIALIZED) {
 775             cmdIndex = getBSDCommandIndex();
 776         }
 777         String[] names = execCmd(lpcAllCom[cmdIndex]);
 778         if (names == null || names.length == 0) {
 779           return null;
 780         }
 781         return names;
 782     }
 783 
 784     static String getDefaultPrinterNameSysV() {
 785         String defaultPrinter = "lp";
 786         String command = "/usr/bin/lpstat -d";
 787 
 788         String [] names = execCmd(command);
 789         if (names == null || names.length == 0) {
 790             return defaultPrinter;
 791         } else {
 792             int index = names[0].indexOf(":");
 793             if (index == -1  || (names[0].length() <= index+1)) {
 794                 return null;
 795             } else {
 796                 String name = names[0].substring(index+1).trim();
 797                 if (name.length() == 0) {
 798                     return null;
 799                 } else {
 800                     return name;
 801                 }
 802             }
 803         }
 804     }
 805 
 806     private PrintService getNamedPrinterNameSysV(String name) {
 807 
 808         String command = "/usr/bin/lpstat -v " + name;
 809         String []result = execCmd(command);
 810 
 811         if (result == null || result[0].indexOf("unknown printer") > 0) {
 812             return null;
 813         } else {
 814             return new UnixPrintService(name);
 815         }
 816     }
 817 
 818     private String[] getAllPrinterNamesSysV() {
 819         String defaultPrinter = "lp";
 820         String command = "/usr/bin/lpstat -v|/usr/bin/expand|/usr/bin/cut -f3 -d' ' |/usr/bin/cut -f1 -d':' | /usr/bin/sort";
 821 
 822         String [] names = execCmd(command);
 823         ArrayList<String> printerNames = new ArrayList<>();
 824         for (int i=0; i < names.length; i++) {
 825             if (!names[i].equals("_default") &&
 826                 !names[i].equals(defaultPrinter) &&
 827                 !names[i].equals("")) {
 828                 printerNames.add(names[i]);
 829             }
 830         }
 831         return printerNames.toArray(new String[printerNames.size()]);
 832     }
 833 
 834     private String getDefaultPrinterNameAIX() {
 835         String[] names = execCmd(lpNameComAix[aix_lpstat_d]);
 836         // Remove headers and bogus entries added by remote printers.
 837         names = UnixPrintService.filterPrinterNamesAIX(names);
 838         if (names == null || names.length != 1) {
 839             // No default printer found
 840             return null;
 841         } else {
 842             return names[0];
 843         }
 844     }
 845 
 846     private PrintService getNamedPrinterNameAIX(String name) {
 847         // On AIX there should be no blank after '-v'.
 848         String[] result = execCmd(lpNameComAix[aix_lpstat_v] + name);
 849         // Remove headers and bogus entries added by remote printers.
 850         result = UnixPrintService.filterPrinterNamesAIX(result);
 851         if (result == null || result.length != 1) {
 852             return null;
 853         } else {
 854             return new UnixPrintService(name);
 855         }
 856     }
 857 
 858     private String[] getAllPrinterNamesAIX() {
 859         // Determine all printers of the system.
 860         String [] names = execCmd(lpNameComAix[aix_defaultPrinterEnumeration]);
 861 
 862         // Remove headers and bogus entries added by remote printers.
 863         names = UnixPrintService.filterPrinterNamesAIX(names);
 864 
 865         ArrayList<String> printerNames = new ArrayList<String>();
 866         for ( int i=0; i < names.length; i++) {
 867             printerNames.add(names[i]);
 868         }
 869         return printerNames.toArray(new String[printerNames.size()]);
 870     }
 871 
 872     static String[] execCmd(final String command) {
 873         ArrayList<String> results = null;
 874         try {
 875             final String[] cmd = new String[3];
 876             if (isSysV() || isAIX()) {
 877                 cmd[0] = "/usr/bin/sh";
 878                 cmd[1] = "-c";
 879                 cmd[2] = "env LC_ALL=C " + command;
 880             } else {
 881                 cmd[0] = "/bin/sh";
 882                 cmd[1] = "-c";
 883                 cmd[2] = "LC_ALL=C " + command;
 884             }
 885 
 886             results = AccessController.doPrivileged(
 887                 new PrivilegedExceptionAction<ArrayList<String>>() {
 888                     public ArrayList<String> run() throws IOException {
 889 
 890                         Process proc;
 891                         BufferedReader bufferedReader = null;
 892                         File f = Files.createTempFile("prn","xc").toFile();
 893                         cmd[2] = cmd[2]+">"+f.getAbsolutePath();
 894 
 895                         proc = Runtime.getRuntime().exec(cmd);
 896                         try {
 897                             boolean done = false; // in case of interrupt.
 898                             while (!done) {
 899                                 try {
 900                                     proc.waitFor();
 901                                     done = true;
 902                                 } catch (InterruptedException e) {
 903                                 }
 904                             }
 905 
 906                             if (proc.exitValue() == 0) {
 907                                 FileReader reader = new FileReader(f);
 908                                 bufferedReader = new BufferedReader(reader);
 909                                 String line;
 910                                 ArrayList<String> results = new ArrayList<>();
 911                                 while ((line = bufferedReader.readLine())
 912                                        != null) {
 913                                     results.add(line);
 914                                 }
 915                                 return results;
 916                             }
 917                         } finally {
 918                             f.delete();
 919                             // promptly close all streams.
 920                             if (bufferedReader != null) {
 921                                 bufferedReader.close();
 922                             }
 923                             proc.getInputStream().close();
 924                             proc.getErrorStream().close();
 925                             proc.getOutputStream().close();
 926                         }
 927                         return null;
 928                     }
 929                 });
 930         } catch (PrivilegedActionException e) {
 931         }
 932         if (results == null) {
 933             return new String[0];
 934         } else {
 935             return results.toArray(new String[results.size()]);
 936         }
 937     }
 938 
 939     private class PrinterChangeListener implements Runnable {
 940 
 941         @Override
 942         public void run() {
 943             int refreshSecs;
 944             while (true) {
 945                 try {
 946                     refreshServices();
 947                 } catch (Exception se) {
 948                     IPPPrintService.debug_println(debugPrefix+"Exception in refresh thread.");
 949                     break;
 950                 }
 951 
 952                 if ((printServices != null) &&
 953                     (printServices.length > minRefreshTime)) {
 954                     // compute new refresh time 1 printer = 1 sec
 955                     refreshSecs = printServices.length;
 956                 } else {
 957                     refreshSecs = minRefreshTime;
 958                 }
 959                 try {
 960                     Thread.sleep(refreshSecs * 1000);
 961                 } catch (InterruptedException e) {
 962                     break;
 963                 }
 964             }
 965         }
 966     }
 967 }