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