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