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