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