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.InputStream;
  32 import java.io.InputStreamReader;
  33 import java.io.IOException;
  34 import java.util.ArrayList;
  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 
  52 public class PrintServiceLookupProvider extends PrintServiceLookup {
  53 
  54     private String defaultPrinter;
  55     private PrintService defaultPrintService;
  56     private String[] printers; /* excludes the default printer */
  57     private PrintService[] printServices; /* includes the default printer */
  58 
  59     static {
  60         java.security.AccessController.doPrivileged(
  61             new java.security.PrivilegedAction<Void>() {
  62                 public Void run() {
  63                     System.loadLibrary("awt");
  64                     return null;
  65                 }
  66             });
  67     }
  68 
  69     /* The singleton win32 print lookup service.
  70      * Code that is aware of this field and wants to use it must first
  71      * see if its null, and if so instantiate it by calling a method such as
  72      * javax.print.PrintServiceLookup.defaultPrintService() so that the
  73      * same instance is stored there.
  74      */
  75     private static PrintServiceLookupProvider win32PrintLUS;
  76 
  77     /* Think carefully before calling this. Preferably don't call it. */
  78     public static PrintServiceLookupProvider getWin32PrintLUS() {
  79         if (win32PrintLUS == null) {
  80             /* This call is internally synchronized.
  81              * When it returns an instance of this class will have
  82              * been instantiated - else there's a JDK internal error.
  83              */
  84             PrintServiceLookup.lookupDefaultPrintService();
  85         }
  86         return win32PrintLUS;
  87     }
  88 
  89     public PrintServiceLookupProvider() {
  90 
  91         if (win32PrintLUS == null) {
  92             win32PrintLUS = this;
  93 
  94             String osName = AccessController.doPrivileged(
  95                 new sun.security.action.GetPropertyAction("os.name"));
  96             // There's no capability for Win98 to refresh printers.
  97             // See "OpenPrinter" for more info.
  98             if (osName != null && osName.startsWith("Windows 98")) {
  99                 return;
 100             }
 101             // start the printer listener thread
 102             Thread thr = new ManagedLocalsThread(new PrinterChangeListener());
 103             thr.setDaemon(true);
 104             thr.start();
 105         } /* else condition ought to never happen! */
 106     }
 107 
 108     /* Want the PrintService which is default print service to have
 109      * equality of reference with the equivalent in list of print services
 110      * This isn't required by the API and there's a risk doing this will
 111      * lead people to assume its guaranteed.
 112      */
 113     public synchronized PrintService[] getPrintServices() {
 114         SecurityManager security = System.getSecurityManager();
 115         if (security != null) {
 116             security.checkPrintJobAccess();
 117         }
 118         if (printServices == null) {
 119             refreshServices();
 120         }
 121         return printServices;
 122     }
 123 
 124     private synchronized void refreshServices() {
 125         printers = getAllPrinterNames();
 126         if (printers == null) {
 127             // In Windows it is safe to assume no default if printers == null so we
 128             // don't get the default.
 129             printServices = new PrintService[0];
 130             return;
 131         }
 132 
 133         PrintService[] newServices = new PrintService[printers.length];
 134         PrintService defService = getDefaultPrintService();
 135         for (int p = 0; p < printers.length; p++) {
 136             if (defService != null &&
 137                 printers[p].equals(defService.getName())) {
 138                 newServices[p] = defService;
 139             } else {
 140                 if (printServices == null) {
 141                     newServices[p] = new Win32PrintService(printers[p]);
 142                 } else {
 143                     int j;
 144                     for (j = 0; j < printServices.length; j++) {
 145                         if ((printServices[j]!= null) &&
 146                             (printers[p].equals(printServices[j].getName()))) {
 147                             newServices[p] = printServices[j];
 148                             printServices[j] = null;
 149                             break;
 150                         }
 151                     }
 152                     if (j == printServices.length) {
 153                         newServices[p] = new Win32PrintService(printers[p]);
 154                     }
 155                 }
 156             }
 157         }
 158 
 159         // Look for deleted services and invalidate these
 160         if (printServices != null) {
 161             for (int j=0; j < printServices.length; j++) {
 162                 if ((printServices[j] instanceof Win32PrintService) &&
 163                     (!printServices[j].equals(defaultPrintService))) {
 164                     ((Win32PrintService)printServices[j]).invalidateService();
 165                 }
 166             }
 167         }
 168         printServices = newServices;
 169     }
 170 
 171 
 172     public synchronized PrintService getPrintServiceByName(String name) {
 173 
 174         if (name == null || name.equals("")) {
 175             return null;
 176         } else {
 177             /* getPrintServices() is now very fast. */
 178             PrintService[] printServices = getPrintServices();
 179             for (int i=0; i<printServices.length; i++) {
 180                 if (printServices[i].getName().equals(name)) {
 181                     return printServices[i];
 182                 }
 183             }
 184             return null;
 185         }
 186     }
 187 
 188     @SuppressWarnings("unchecked") // Cast to Class<PrintServiceAttribute>
 189     boolean matchingService(PrintService service,
 190                             PrintServiceAttributeSet serviceSet) {
 191         if (serviceSet != null) {
 192             Attribute [] attrs =  serviceSet.toArray();
 193             Attribute serviceAttr;
 194             for (int i=0; i<attrs.length; i++) {
 195                 serviceAttr
 196                     = service.getAttribute((Class<PrintServiceAttribute>)attrs[i].getCategory());
 197                 if (serviceAttr == null || !serviceAttr.equals(attrs[i])) {
 198                     return false;
 199                 }
 200             }
 201         }
 202         return true;
 203     }
 204 
 205     public PrintService[] getPrintServices(DocFlavor flavor,
 206                                            AttributeSet attributes) {
 207 
 208         SecurityManager security = System.getSecurityManager();
 209         if (security != null) {
 210           security.checkPrintJobAccess();
 211         }
 212         PrintRequestAttributeSet requestSet = null;
 213         PrintServiceAttributeSet serviceSet = null;
 214 
 215         if (attributes != null && !attributes.isEmpty()) {
 216 
 217             requestSet = new HashPrintRequestAttributeSet();
 218             serviceSet = new HashPrintServiceAttributeSet();
 219 
 220             Attribute[] attrs = attributes.toArray();
 221             for (int i=0; i<attrs.length; i++) {
 222                 if (attrs[i] instanceof PrintRequestAttribute) {
 223                     requestSet.add(attrs[i]);
 224                 } else if (attrs[i] instanceof PrintServiceAttribute) {
 225                     serviceSet.add(attrs[i]);
 226                 }
 227             }
 228         }
 229 
 230         /*
 231          * Special case: If client is asking for a particular printer
 232          * (by name) then we can save time by getting just that service
 233          * to check against the rest of the specified attributes.
 234          */
 235         PrintService[] services = null;
 236         if (serviceSet != null && serviceSet.get(PrinterName.class) != null) {
 237             PrinterName name = (PrinterName)serviceSet.get(PrinterName.class);
 238             PrintService service = getPrintServiceByName(name.getValue());
 239             if (service == null || !matchingService(service, serviceSet)) {
 240                 services = new PrintService[0];
 241             } else {
 242                 services = new PrintService[1];
 243                 services[0] = service;
 244             }
 245         } else {
 246             services = getPrintServices();
 247         }
 248 
 249         if (services.length == 0) {
 250             return services;
 251         } else {
 252             ArrayList<PrintService> matchingServices = new ArrayList<>();
 253             for (int i=0; i<services.length; i++) {
 254                 try {
 255                     if (services[i].
 256                         getUnsupportedAttributes(flavor, requestSet) == null) {
 257                         matchingServices.add(services[i]);
 258                     }
 259                 } catch (IllegalArgumentException e) {
 260                 }
 261             }
 262             services = new PrintService[matchingServices.size()];
 263             return matchingServices.toArray(services);
 264         }
 265     }
 266 
 267     /*
 268      * return empty array as don't support multi docs
 269      */
 270     public MultiDocPrintService[]
 271         getMultiDocPrintServices(DocFlavor[] flavors,
 272                                  AttributeSet attributes) {
 273         SecurityManager security = System.getSecurityManager();
 274         if (security != null) {
 275           security.checkPrintJobAccess();
 276         }
 277         return new MultiDocPrintService[0];
 278     }
 279 
 280 
 281     public synchronized PrintService getDefaultPrintService() {
 282         SecurityManager security = System.getSecurityManager();
 283         if (security != null) {
 284           security.checkPrintJobAccess();
 285         }
 286 
 287 
 288         // Windows does not have notification for a change in default
 289         // so we always get the latest.
 290         defaultPrinter = getDefaultPrinterName();
 291         if (defaultPrinter == null) {
 292             return null;
 293         }
 294 
 295         if ((defaultPrintService != null) &&
 296             defaultPrintService.getName().equals(defaultPrinter)) {
 297 
 298             return defaultPrintService;
 299         }
 300 
 301          // Not the same as default so proceed to get new PrintService.
 302 
 303         // clear defaultPrintService
 304         defaultPrintService = null;
 305 
 306         if (printServices != null) {
 307             for (int j=0; j<printServices.length; j++) {
 308                 if (defaultPrinter.equals(printServices[j].getName())) {
 309                     defaultPrintService = printServices[j];
 310                     break;
 311                 }
 312             }
 313         }
 314 
 315         if (defaultPrintService == null) {
 316             defaultPrintService = new Win32PrintService(defaultPrinter);
 317         }
 318         return defaultPrintService;
 319     }
 320 
 321     class PrinterChangeListener implements Runnable {
 322         long chgObj;
 323         PrinterChangeListener() {
 324             chgObj = notifyFirstPrinterChange(null);
 325         }
 326 
 327         @Override
 328         public void run() {
 329             if (chgObj != -1) {
 330                 while (true) {
 331                     // wait for configuration to change
 332                     if (notifyPrinterChange(chgObj) != 0) {
 333                         try {
 334                             refreshServices();
 335                         } catch (SecurityException se) {
 336                             break;
 337                         }
 338                     } else {
 339                         notifyClosePrinterChange(chgObj);
 340                         break;
 341                     }
 342                 }
 343             }
 344         }
 345     }
 346 
 347     private native String getDefaultPrinterName();
 348     private native String[] getAllPrinterNames();
 349     private native long notifyFirstPrinterChange(String printer);
 350     private native void notifyClosePrinterChange(long chgObj);
 351     private native int notifyPrinterChange(long chgObj);
 352 }