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