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