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