1 /*
   2  * Copyright (c) 2000, 2007, 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 Win32PrintServiceLookup 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 Win32PrintServiceLookup win32PrintLUS;
  74 
  75     /* Think carefully before calling this. Preferably don't call it. */
  76     public static Win32PrintServiceLookup 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 Win32PrintServiceLookup() {
  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             PrinterChangeListener thr = new PrinterChangeListener();
 101             thr.setDaemon(true);
 102             thr.start();
 103         } /* else condition ought to never happen! */
 104     }
 105 
 106     /* Want the PrintService which is default print service to have
 107      * equality of reference with the equivalent in list of print services
 108      * This isn't required by the API and there's a risk doing this will
 109      * lead people to assume its guaranteed.
 110      */
 111     public synchronized PrintService[] getPrintServices() {
 112         SecurityManager security = System.getSecurityManager();
 113         if (security != null) {
 114             security.checkPrintJobAccess();
 115         }
 116         if (printServices == null) {
 117             refreshServices();
 118         }
 119         return printServices;
 120     }
 121 
 122     private synchronized void refreshServices() {
 123         printers = getAllPrinterNames();
 124         if (printers == null) {
 125             // In Windows it is safe to assume no default if printers == null so we
 126             // don't get the default.
 127             printServices = new PrintService[0];
 128             return;
 129         }
 130 
 131         PrintService[] newServices = new PrintService[printers.length];
 132         PrintService defService = getDefaultPrintService();
 133         for (int p = 0; p < printers.length; p++) {
 134             if (defService != null &&
 135                 printers[p].equals(defService.getName())) {
 136                 newServices[p] = defService;
 137             } else {
 138                 if (printServices == null) {
 139                     newServices[p] = new Win32PrintService(printers[p]);
 140                 } else {
 141                     int j;
 142                     for (j = 0; j < printServices.length; j++) {
 143                         if ((printServices[j]!= null) &&
 144                             (printers[p].equals(printServices[j].getName()))) {
 145                             newServices[p] = printServices[j];
 146                             printServices[j] = null;
 147                             break;
 148                         }
 149                     }
 150                     if (j == printServices.length) {
 151                         newServices[p] = new Win32PrintService(printers[p]);
 152                     }
 153                 }
 154             }
 155         }
 156 
 157         // Look for deleted services and invalidate these
 158         if (printServices != null) {
 159             for (int j=0; j < printServices.length; j++) {
 160                 if ((printServices[j] instanceof Win32PrintService) &&
 161                     (!printServices[j].equals(defaultPrintService))) {
 162                     ((Win32PrintService)printServices[j]).invalidateService();
 163                 }
 164             }
 165         }
 166         printServices = newServices;
 167     }
 168 
 169 
 170     public synchronized PrintService getPrintServiceByName(String name) {
 171 
 172         if (name == null || name.equals("")) {
 173             return null;
 174         } else {
 175             /* getPrintServices() is now very fast. */
 176             PrintService[] printServices = getPrintServices();
 177             for (int i=0; i<printServices.length; i++) {
 178                 if (printServices[i].getName().equals(name)) {
 179                     return printServices[i];
 180                 }
 181             }
 182             return null;
 183         }
 184     }
 185 
 186     boolean matchingService(PrintService service,
 187                             PrintServiceAttributeSet serviceSet) {
 188         if (serviceSet != null) {
 189             Attribute [] attrs =  serviceSet.toArray();
 190             Attribute serviceAttr;
 191             for (int i=0; i<attrs.length; i++) {
 192                 serviceAttr
 193                     = service.getAttribute((Class<PrintServiceAttribute>)attrs[i].getCategory());
 194                 if (serviceAttr == null || !serviceAttr.equals(attrs[i])) {
 195                     return false;
 196                 }
 197             }
 198         }
 199         return true;
 200     }
 201 
 202     public PrintService[] getPrintServices(DocFlavor flavor,
 203                                            AttributeSet attributes) {
 204 
 205         SecurityManager security = System.getSecurityManager();
 206         if (security != null) {
 207           security.checkPrintJobAccess();
 208         }
 209         PrintRequestAttributeSet requestSet = null;
 210         PrintServiceAttributeSet serviceSet = null;
 211 
 212         if (attributes != null && !attributes.isEmpty()) {
 213 
 214             requestSet = new HashPrintRequestAttributeSet();
 215             serviceSet = new HashPrintServiceAttributeSet();
 216 
 217             Attribute[] attrs = attributes.toArray();
 218             for (int i=0; i<attrs.length; i++) {
 219                 if (attrs[i] instanceof PrintRequestAttribute) {
 220                     requestSet.add(attrs[i]);
 221                 } else if (attrs[i] instanceof PrintServiceAttribute) {
 222                     serviceSet.add(attrs[i]);
 223                 }
 224             }
 225         }
 226 
 227         /*
 228          * Special case: If client is asking for a particular printer
 229          * (by name) then we can save time by getting just that service
 230          * to check against the rest of the specified attributes.
 231          */
 232         PrintService[] services = null;
 233         if (serviceSet != null && serviceSet.get(PrinterName.class) != null) {
 234             PrinterName name = (PrinterName)serviceSet.get(PrinterName.class);
 235             PrintService service = getPrintServiceByName(name.getValue());
 236             if (service == null || !matchingService(service, serviceSet)) {
 237                 services = new PrintService[0];
 238             } else {
 239                 services = new PrintService[1];
 240                 services[0] = service;
 241             }
 242         } else {
 243             services = getPrintServices();
 244         }
 245 
 246         if (services.length == 0) {
 247             return services;
 248         } else {
 249             ArrayList matchingServices = new ArrayList();
 250             for (int i=0; i<services.length; i++) {
 251                 try {
 252                     if (services[i].
 253                         getUnsupportedAttributes(flavor, requestSet) == null) {
 254                         matchingServices.add(services[i]);
 255                     }
 256                 } catch (IllegalArgumentException e) {
 257                 }
 258             }
 259             services = new PrintService[matchingServices.size()];
 260             return (PrintService[])matchingServices.toArray(services);
 261         }
 262     }
 263 
 264     /*
 265      * return empty array as don't support multi docs
 266      */
 267     public MultiDocPrintService[]
 268         getMultiDocPrintServices(DocFlavor[] flavors,
 269                                  AttributeSet attributes) {
 270         SecurityManager security = System.getSecurityManager();
 271         if (security != null) {
 272           security.checkPrintJobAccess();
 273         }
 274         return new MultiDocPrintService[0];
 275     }
 276 
 277 
 278     public synchronized PrintService getDefaultPrintService() {
 279         SecurityManager security = System.getSecurityManager();
 280         if (security != null) {
 281           security.checkPrintJobAccess();
 282         }
 283 
 284 
 285         // Windows does not have notification for a change in default
 286         // so we always get the latest.
 287         defaultPrinter = getDefaultPrinterName();
 288         if (defaultPrinter == null) {
 289             return null;
 290         }
 291 
 292         if ((defaultPrintService != null) &&
 293             defaultPrintService.getName().equals(defaultPrinter)) {
 294 
 295             return defaultPrintService;
 296         }
 297 
 298          // Not the same as default so proceed to get new PrintService.
 299 
 300         // clear defaultPrintService
 301         defaultPrintService = null;
 302 
 303         if (printServices != null) {
 304             for (int j=0; j<printServices.length; j++) {
 305                 if (defaultPrinter.equals(printServices[j].getName())) {
 306                     defaultPrintService = printServices[j];
 307                     break;
 308                 }
 309             }
 310         }
 311 
 312         if (defaultPrintService == null) {
 313             defaultPrintService = new Win32PrintService(defaultPrinter);
 314         }
 315         return defaultPrintService;
 316     }
 317 
 318     class PrinterChangeListener extends Thread {
 319         long chgObj;
 320         PrinterChangeListener() {
 321             chgObj = notifyFirstPrinterChange(null);
 322         }
 323 
 324         public void run() {
 325             if (chgObj != -1) {
 326                 while (true) {
 327                     // wait for configuration to change
 328                     if (notifyPrinterChange(chgObj) != 0) {
 329                         try {
 330                             refreshServices();
 331                         } catch (SecurityException se) {
 332                             break;
 333                         }
 334                     } else {
 335                         notifyClosePrinterChange(chgObj);
 336                         break;
 337                     }
 338                 }
 339             }
 340         }
 341     }
 342 
 343     private native String getDefaultPrinterName();
 344     private native String[] getAllPrinterNames();
 345     private native long notifyFirstPrinterChange(String printer);
 346     private native void notifyClosePrinterChange(long chgObj);
 347     private native int notifyPrinterChange(long chgObj);
 348 }