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