1 /*
   2  * Copyright (c) 2003, 2014, 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.net.URL;
  29 import java.net.HttpURLConnection;
  30 import java.io.OutputStream;
  31 import java.io.InputStream;
  32 import java.util.ArrayList;
  33 import java.util.HashMap;
  34 import sun.print.IPPPrintService;
  35 import sun.print.CustomMediaSizeName;
  36 import sun.print.CustomMediaTray;
  37 import javax.print.attribute.standard.Media;
  38 import javax.print.attribute.standard.MediaSizeName;
  39 import javax.print.attribute.standard.MediaSize;
  40 import javax.print.attribute.standard.MediaTray;
  41 import javax.print.attribute.standard.MediaPrintableArea;
  42 import javax.print.attribute.standard.PrinterResolution;
  43 import javax.print.attribute.Size2DSyntax;
  44 import javax.print.attribute.Attribute;
  45 import javax.print.attribute.EnumSyntax;
  46 import javax.print.attribute.standard.PrinterName;
  47 
  48 
  49 public class CUPSPrinter  {
  50     private static final String debugPrefix = "CUPSPrinter>> ";
  51     private static final double PRINTER_DPI = 72.0;
  52     private boolean initialized;
  53     private static native String getCupsServer();
  54     private static native int getCupsPort();
  55     private static native String getCupsDefaultPrinter();
  56     private static native boolean canConnect(String server, int port);
  57     private static native boolean initIDs();
  58     // These functions need to be synchronized as
  59     // CUPS does not support multi-threading.
  60     private static synchronized native String[] getMedia(String printer);
  61     private static synchronized native float[] getPageSizes(String printer);
  62     private static synchronized native void
  63         getResolutions(String printer, ArrayList<Integer> resolutionList);
  64     //public static boolean useIPPMedia = false; will be used later
  65 
  66     private MediaPrintableArea[] cupsMediaPrintables;
  67     private MediaSizeName[] cupsMediaSNames;
  68     private CustomMediaSizeName[] cupsCustomMediaSNames;
  69     private MediaTray[] cupsMediaTrays;
  70 
  71     public  int nPageSizes = 0;
  72     public  int nTrays = 0;
  73     private  String[] media;
  74     private  float[] pageSizes;
  75     int[]   resolutionsArray;
  76     private String printer;
  77 
  78     private static boolean libFound;
  79     private static String cupsServer = null;
  80     private static int cupsPort = 0;
  81 
  82     static {
  83         // load awt library to access native code
  84         java.security.AccessController.doPrivileged(
  85             new java.security.PrivilegedAction<Void>() {
  86                 public Void run() {
  87                     System.loadLibrary("awt");
  88                     return null;
  89                 }
  90             });
  91         libFound = initIDs();
  92         if (libFound) {
  93            cupsServer = getCupsServer();
  94            cupsPort = getCupsPort();
  95         }
  96     }
  97 
  98 
  99     CUPSPrinter (String printerName) {
 100         if (printerName == null) {
 101             throw new IllegalArgumentException("null printer name");
 102         }
 103         printer = printerName;
 104         cupsMediaSNames = null;
 105         cupsMediaPrintables = null;
 106         cupsMediaTrays = null;
 107         initialized = false;
 108 
 109         if (!libFound) {
 110             throw new RuntimeException("cups lib not found");
 111         } else {
 112             // get page + tray names
 113             media =  getMedia(printer);
 114             if (media == null) {
 115                 // either PPD file is not found or printer is unknown
 116                 throw new RuntimeException("error getting PPD");
 117             }
 118 
 119             // get sizes
 120             pageSizes = getPageSizes(printer);
 121             if (pageSizes != null) {
 122                 nPageSizes = pageSizes.length/6;
 123 
 124                 nTrays = media.length/2-nPageSizes;
 125                 assert (nTrays >= 0);
 126             }
 127             ArrayList<Integer> resolutionList = new ArrayList<>();
 128             getResolutions(printer, resolutionList);
 129             resolutionsArray = new int[resolutionList.size()];
 130             for (int i=0; i < resolutionList.size(); i++) {
 131                 resolutionsArray[i] = resolutionList.get(i);
 132             }
 133         }
 134     }
 135 
 136 
 137     /**
 138      * Returns array of MediaSizeNames derived from PPD.
 139      */
 140     MediaSizeName[] getMediaSizeNames() {
 141         initMedia();
 142         return cupsMediaSNames;
 143     }
 144 
 145 
 146     /**
 147      * Returns array of Custom MediaSizeNames derived from PPD.
 148      */
 149     CustomMediaSizeName[] getCustomMediaSizeNames() {
 150         initMedia();
 151         return cupsCustomMediaSNames;
 152     }
 153 
 154     public int getDefaultMediaIndex() {
 155         return ((pageSizes.length >1) ? (int)(pageSizes[pageSizes.length -1]) : 0);
 156     }
 157 
 158     /**
 159      * Returns array of MediaPrintableArea derived from PPD.
 160      */
 161     MediaPrintableArea[] getMediaPrintableArea() {
 162         initMedia();
 163         return cupsMediaPrintables;
 164     }
 165 
 166     /**
 167      * Returns array of MediaTrays derived from PPD.
 168      */
 169     MediaTray[] getMediaTrays() {
 170         initMedia();
 171         return cupsMediaTrays;
 172     }
 173 
 174     /**
 175      * return the raw packed array of supported printer resolutions.
 176      */
 177     int[] getRawResolutions() {
 178         return resolutionsArray;
 179     }
 180 
 181     /**
 182      * Initialize media by translating PPD info to PrintService attributes.
 183      */
 184     private synchronized void initMedia() {
 185         if (initialized) {
 186             return;
 187         } else {
 188             initialized = true;
 189         }
 190 
 191         if (pageSizes == null) {
 192             return;
 193         }
 194 
 195         cupsMediaPrintables = new MediaPrintableArea[nPageSizes];
 196         cupsMediaSNames = new MediaSizeName[nPageSizes];
 197         cupsCustomMediaSNames = new CustomMediaSizeName[nPageSizes];
 198 
 199         CustomMediaSizeName msn;
 200         MediaPrintableArea mpa;
 201         float length, width, x, y, w, h;
 202 
 203         // initialize names and printables
 204         for (int i=0; i<nPageSizes; i++) {
 205             // media width and length
 206             width = (float)(pageSizes[i*6]/PRINTER_DPI);
 207             length = (float)(pageSizes[i*6+1]/PRINTER_DPI);
 208             // media printable area
 209             x = (float)(pageSizes[i*6+2]/PRINTER_DPI);
 210             h = (float)(pageSizes[i*6+3]/PRINTER_DPI);
 211             w = (float)(pageSizes[i*6+4]/PRINTER_DPI);
 212             y = (float)(pageSizes[i*6+5]/PRINTER_DPI);
 213 
 214             msn = new CustomMediaSizeName(media[i*2], media[i*2+1],
 215                                           width, length);
 216 
 217             // add to list of standard MediaSizeNames
 218             if ((cupsMediaSNames[i] = msn.getStandardMedia()) == null) {
 219                 // add custom if no matching standard media
 220                 cupsMediaSNames[i] = msn;
 221 
 222                 // add this new custom msn to MediaSize array
 223                 if ((width > 0.0) && (length > 0.0)) {
 224                     try {
 225                     new MediaSize(width, length,
 226                                   Size2DSyntax.INCH, msn);
 227                     } catch (IllegalArgumentException e) {
 228                         /* PDF printer in Linux for Ledger paper causes
 229                         "IllegalArgumentException: X dimension > Y dimension".
 230                         We rotate based on IPP spec. */
 231                         new MediaSize(length, width, Size2DSyntax.INCH, msn);
 232                     }
 233                 }
 234             }
 235 
 236             // add to list of custom MediaSizeName
 237             // for internal use of IPPPrintService
 238             cupsCustomMediaSNames[i] = msn;
 239 
 240             mpa = null;
 241             try {
 242                 mpa = new MediaPrintableArea(x, y, w, h,
 243                                              MediaPrintableArea.INCH);
 244             } catch (IllegalArgumentException e) {
 245                 if (width > 0 && length > 0) {
 246                     mpa = new MediaPrintableArea(0, 0, width, length,
 247                                              MediaPrintableArea.INCH);
 248                 }
 249             }
 250             cupsMediaPrintables[i] = mpa;
 251         }
 252 
 253         // initialize trays
 254         cupsMediaTrays = new MediaTray[nTrays];
 255 
 256         MediaTray mt;
 257         for (int i=0; i<nTrays; i++) {
 258             mt = new CustomMediaTray(media[(nPageSizes+i)*2],
 259                                      media[(nPageSizes+i)*2+1]);
 260             cupsMediaTrays[i] = mt;
 261         }
 262 
 263     }
 264 
 265     /**
 266      * Get CUPS default printer using IPP.
 267      * Returns 2 values - index 0 is printer name, index 1 is the uri.
 268      */
 269     static String[] getDefaultPrinter() {
 270         // Try to get user/lpoptions-defined printer name from CUPS
 271         // if not user-set, then go for server default destination
 272         String printerInfo[] = new String[2];
 273         printerInfo[0] = getCupsDefaultPrinter();
 274 
 275         if (printerInfo[0] != null) {
 276             printerInfo[1] = null;
 277             return printerInfo.clone();
 278         }
 279         try {
 280             URL url = new URL("http", getServer(), getPort(), "");
 281             final HttpURLConnection urlConnection =
 282                 IPPPrintService.getIPPConnection(url);
 283 
 284             if (urlConnection != null) {
 285                 OutputStream os = java.security.AccessController.
 286                     doPrivileged(new java.security.PrivilegedAction<OutputStream>() {
 287                         public OutputStream run() {
 288                             try {
 289                                 return urlConnection.getOutputStream();
 290                             } catch (Exception e) {
 291                                IPPPrintService.debug_println(debugPrefix+e);
 292                             }
 293                             return null;
 294                         }
 295                     });
 296 
 297                 if (os == null) {
 298                     return null;
 299                 }
 300 
 301                 AttributeClass attCl[] = {
 302                     AttributeClass.ATTRIBUTES_CHARSET,
 303                     AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE,
 304                     new AttributeClass("requested-attributes",
 305                                        AttributeClass.TAG_URI,
 306                                        "printer-uri")
 307                 };
 308 
 309                 if (IPPPrintService.writeIPPRequest(os,
 310                                         IPPPrintService.OP_CUPS_GET_DEFAULT,
 311                                         attCl)) {
 312 
 313                     HashMap<String, AttributeClass> defaultMap = null;
 314 
 315                     InputStream is = urlConnection.getInputStream();
 316                     HashMap<String, AttributeClass>[] responseMap = IPPPrintService.readIPPResponse(
 317                                          is);
 318                     is.close();
 319 
 320                     if (responseMap != null && responseMap.length > 0) {
 321                         defaultMap = responseMap[0];
 322                     } else {
 323                        IPPPrintService.debug_println(debugPrefix+
 324                            " empty response map for GET_DEFAULT.");
 325                     }
 326 
 327                     if (defaultMap == null) {
 328                         os.close();
 329                         urlConnection.disconnect();
 330 
 331                         /* CUPS on OS X, as initially configured, considers the
 332                          * default printer to be the last one used that's
 333                          * presently available. So if no default was
 334                          * reported, exec lpstat -d which has all the Apple
 335                          * special behaviour for this built in.
 336                          */
 337                          if (PrintServiceLookupProvider.isMac()) {
 338                              printerInfo[0] = PrintServiceLookupProvider.
 339                                                    getDefaultPrinterNameSysV();
 340                              printerInfo[1] = null;
 341                              return printerInfo.clone();
 342                          } else {
 343                              return null;
 344                          }
 345                     }
 346 
 347 
 348                     AttributeClass attribClass = defaultMap.get("printer-name");
 349 
 350                     if (attribClass != null) {
 351                         printerInfo[0] = attribClass.getStringValue();
 352                         attribClass = defaultMap.get("printer-uri-supported");
 353                         IPPPrintService.debug_println(debugPrefix+
 354                           "printer-uri-supported="+attribClass);
 355                         if (attribClass != null) {
 356                             printerInfo[1] = attribClass.getStringValue();
 357                         } else {
 358                             printerInfo[1] = null;
 359                         }
 360                         os.close();
 361                         urlConnection.disconnect();
 362                         return printerInfo.clone();
 363                     }
 364                 }
 365                 os.close();
 366                 urlConnection.disconnect();
 367             }
 368         } catch (Exception e) {
 369         }
 370         return null;
 371     }
 372 
 373 
 374     /**
 375      * Get list of all CUPS printers using IPP.
 376      */
 377     static String[] getAllPrinters() {
 378         try {
 379             URL url = new URL("http", getServer(), getPort(), "");
 380 
 381             final HttpURLConnection urlConnection =
 382                 IPPPrintService.getIPPConnection(url);
 383 
 384             if (urlConnection != null) {
 385                 OutputStream os = java.security.AccessController.
 386                     doPrivileged(new java.security.PrivilegedAction<OutputStream>() {
 387                         public OutputStream run() {
 388                             try {
 389                                 return urlConnection.getOutputStream();
 390                             } catch (Exception e) {
 391                             }
 392                             return null;
 393                         }
 394                     });
 395 
 396                 if (os == null) {
 397                     return null;
 398                 }
 399 
 400                 AttributeClass attCl[] = {
 401                     AttributeClass.ATTRIBUTES_CHARSET,
 402                     AttributeClass.ATTRIBUTES_NATURAL_LANGUAGE,
 403                     new AttributeClass("requested-attributes",
 404                                        AttributeClass.TAG_KEYWORD,
 405                                        "printer-uri-supported")
 406                 };
 407 
 408                 if (IPPPrintService.writeIPPRequest(os,
 409                                 IPPPrintService.OP_CUPS_GET_PRINTERS, attCl)) {
 410 
 411                     InputStream is = urlConnection.getInputStream();
 412                     HashMap<String, AttributeClass>[] responseMap =
 413                         IPPPrintService.readIPPResponse(is);
 414 
 415                     is.close();
 416                     os.close();
 417                     urlConnection.disconnect();
 418 
 419                     if (responseMap == null || responseMap.length == 0) {
 420                         return null;
 421                     }
 422 
 423                     ArrayList<String> printerNames = new ArrayList<>();
 424                     for (int i=0; i< responseMap.length; i++) {
 425                         AttributeClass attribClass =
 426                             responseMap[i].get("printer-uri-supported");
 427 
 428                         if (attribClass != null) {
 429                             String nameStr = attribClass.getStringValue();
 430                             printerNames.add(nameStr);
 431                         }
 432                     }
 433                     return printerNames.toArray(new String[] {});
 434                 } else {
 435                     os.close();
 436                     urlConnection.disconnect();
 437                 }
 438             }
 439 
 440         } catch (Exception e) {
 441         }
 442         return null;
 443 
 444     }
 445 
 446     /**
 447      * Returns CUPS server name.
 448      */
 449     public static String getServer() {
 450         return cupsServer;
 451     }
 452 
 453     /**
 454      * Returns CUPS port number.
 455      */
 456     public static int getPort() {
 457         return cupsPort;
 458     }
 459 
 460     /**
 461      * Detects if CUPS is running.
 462      */
 463     public static boolean isCupsRunning() {
 464         IPPPrintService.debug_println(debugPrefix+"libFound "+libFound);
 465         if (libFound) {
 466             IPPPrintService.debug_println(debugPrefix+"CUPS server "+getServer()+
 467                                           " port "+getPort());
 468             return canConnect(getServer(), getPort());
 469         } else {
 470             return false;
 471         }
 472     }
 473 
 474 
 475 }