1 /*
2  * Copyright (c) 2000, 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.URI;
29 import java.net.URL;
30 import java.io.BufferedInputStream;
31 import java.io.BufferedOutputStream;
32 import java.io.BufferedReader;
33 import java.io.BufferedWriter;
34 import java.io.File;
35 import java.io.FileOutputStream;
36 import java.io.InputStream;
37 import java.io.InputStreamReader;
38 import java.io.OutputStream;
39 import java.io.OutputStreamWriter;
40 import java.io.IOException;
41 import java.io.PrintWriter;
42 import java.io.Reader;
43 import java.io.StringWriter;
44 import java.io.UnsupportedEncodingException;
45 import java.nio.file.Files;
46 import java.util.Vector;
47 
48 import javax.print.CancelablePrintJob;
49 import javax.print.Doc;
50 import javax.print.DocFlavor;
51 import javax.print.DocPrintJob;
52 import javax.print.PrintService;
53 import javax.print.PrintException;
54 import javax.print.event.PrintJobEvent;
55 import javax.print.event.PrintJobListener;
56 import javax.print.event.PrintJobAttributeListener;
57 
58 import javax.print.attribute.Attribute;
59 import javax.print.attribute.AttributeSet;
60 import javax.print.attribute.AttributeSetUtilities;
61 import javax.print.attribute.DocAttributeSet;
62 import javax.print.attribute.HashPrintJobAttributeSet;
63 import javax.print.attribute.HashPrintRequestAttributeSet;
64 import javax.print.attribute.PrintJobAttribute;
65 import javax.print.attribute.PrintJobAttributeSet;
66 import javax.print.attribute.PrintRequestAttribute;
67 import javax.print.attribute.PrintRequestAttributeSet;
68 import javax.print.attribute.PrintServiceAttributeSet;
69 import javax.print.attribute.standard.Copies;
70 import javax.print.attribute.standard.Destination;
71 import javax.print.attribute.standard.DocumentName;
72 import javax.print.attribute.standard.Fidelity;
73 import javax.print.attribute.standard.JobName;
74 import javax.print.attribute.standard.JobOriginatingUserName;
75 import javax.print.attribute.standard.JobSheets;
76 import javax.print.attribute.standard.Media;
77 import javax.print.attribute.standard.MediaSize;
78 import javax.print.attribute.standard.MediaSizeName;
79 import javax.print.attribute.standard.OrientationRequested;
80 import javax.print.attribute.standard.PrinterName;
81 import javax.print.attribute.standard.RequestingUserName;
82 import javax.print.attribute.standard.NumberUp;
83 import javax.print.attribute.standard.Sides;
84 import javax.print.attribute.standard.PrinterIsAcceptingJobs;
85 
86 import java.awt.print.*;
87 
88 
89 
90 public class UnixPrintJob implements CancelablePrintJob {
91     private static String debugPrefix = "UnixPrintJob>> ";
92 
93     transient private Vector jobListeners;
94     transient private Vector attrListeners;
95     transient private Vector listenedAttributeSets;
96 
97     private PrintService service;
98     private boolean fidelity;
99     private boolean printing = false;
100     private boolean printReturned = false;
101     private PrintRequestAttributeSet reqAttrSet = null;
102     private PrintJobAttributeSet jobAttrSet = null;
103     private PrinterJob job;
104     private Doc doc;
105     /* these variables used globally to store reference to the print
106      * data retrieved as a stream. On completion these are always closed
107      * if non-null.
108      */
109     private InputStream instream = null;
110     private Reader reader = null;
111 
112     /* default values overridden by those extracted from the attributes */
113     private String jobName = "Java Printing";
114     private int copies = 1;
115     private MediaSizeName mediaName = MediaSizeName.NA_LETTER;
116     private MediaSize     mediaSize = MediaSize.NA.LETTER;
117     private CustomMediaTray     customTray = null;
118     private OrientationRequested orient = OrientationRequested.PORTRAIT;
119     private NumberUp nUp = null;
120     private Sides sides = null;
121 
UnixPrintJob(PrintService service)122     UnixPrintJob(PrintService service) {
123         this.service = service;
124         mDestination = service.getName();
125         if (PrintServiceLookupProvider.isMac()) {
126             mDestination = ((IPPPrintService)service).getDest();
127         }
128         mDestType = UnixPrintJob.DESTPRINTER;
129     }
130 
getPrintService()131     public PrintService getPrintService() {
132         return service;
133     }
134 
getAttributes()135     public PrintJobAttributeSet getAttributes() {
136         synchronized (this) {
137             if (jobAttrSet == null) {
138                 /* just return an empty set until the job is submitted */
139                 PrintJobAttributeSet jobSet = new HashPrintJobAttributeSet();
140                 return AttributeSetUtilities.unmodifiableView(jobSet);
141             } else {
142               return jobAttrSet;
143             }
144         }
145     }
146 
addPrintJobListener(PrintJobListener listener)147     public void addPrintJobListener(PrintJobListener listener) {
148         synchronized (this) {
149             if (listener == null) {
150                 return;
151             }
152             if (jobListeners == null) {
153                 jobListeners = new Vector();
154             }
155             jobListeners.add(listener);
156         }
157     }
158 
removePrintJobListener(PrintJobListener listener)159     public void removePrintJobListener(PrintJobListener listener) {
160         synchronized (this) {
161             if (listener == null || jobListeners == null ) {
162                 return;
163             }
164             jobListeners.remove(listener);
165             if (jobListeners.isEmpty()) {
166                 jobListeners = null;
167             }
168         }
169     }
170 
171 
172     /* Closes any stream already retrieved for the data.
173      * We want to avoid unnecessarily asking the Doc to create a stream only
174      * to get a reference in order to close it because the job failed.
175      * If the representation class is itself a "stream", this
176      * closes that stream too.
177      */
closeDataStreams()178     private void closeDataStreams() {
179 
180         if (doc == null) {
181             return;
182         }
183 
184         Object data = null;
185 
186         try {
187             data = doc.getPrintData();
188         } catch (IOException e) {
189             return;
190         }
191 
192         if (instream != null) {
193             try {
194                 instream.close();
195             } catch (IOException e) {
196             } finally {
197                 instream = null;
198             }
199         }
200         else if (reader != null) {
201             try {
202                 reader.close();
203             } catch (IOException e) {
204             } finally {
205                 reader = null;
206             }
207         }
208         else if (data instanceof InputStream) {
209             try {
210                 ((InputStream)data).close();
211             } catch (IOException e) {
212             }
213         }
214         else if (data instanceof Reader) {
215             try {
216                 ((Reader)data).close();
217             } catch (IOException e) {
218             }
219         }
220     }
221 
notifyEvent(int reason)222     private void notifyEvent(int reason) {
223 
224         /* since this method should always get called, here's where
225          * we will perform the clean up of any data stream supplied.
226          */
227         switch (reason) {
228             case PrintJobEvent.DATA_TRANSFER_COMPLETE:
229             case PrintJobEvent.JOB_CANCELED :
230             case PrintJobEvent.JOB_FAILED :
231             case PrintJobEvent.NO_MORE_EVENTS :
232             case PrintJobEvent.JOB_COMPLETE :
233                 closeDataStreams();
234         }
235 
236         synchronized (this) {
237             if (jobListeners != null) {
238                 PrintJobListener listener;
239                 PrintJobEvent event = new PrintJobEvent(this, reason);
240                 for (int i = 0; i < jobListeners.size(); i++) {
241                     listener = (PrintJobListener)(jobListeners.elementAt(i));
242                     switch (reason) {
243 
244                         case PrintJobEvent.JOB_CANCELED :
245                             listener.printJobCanceled(event);
246                             break;
247 
248                         case PrintJobEvent.JOB_FAILED :
249                             listener.printJobFailed(event);
250                             break;
251 
252                         case PrintJobEvent.DATA_TRANSFER_COMPLETE :
253                             listener.printDataTransferCompleted(event);
254                             break;
255 
256                         case PrintJobEvent.NO_MORE_EVENTS :
257                             listener.printJobNoMoreEvents(event);
258                             break;
259 
260                         default:
261                             break;
262                     }
263                 }
264             }
265        }
266     }
267 
addPrintJobAttributeListener( PrintJobAttributeListener listener, PrintJobAttributeSet attributes)268     public void addPrintJobAttributeListener(
269                                   PrintJobAttributeListener listener,
270                                   PrintJobAttributeSet attributes) {
271         synchronized (this) {
272             if (listener == null) {
273                 return;
274             }
275             if (attrListeners == null) {
276                 attrListeners = new Vector();
277                 listenedAttributeSets = new Vector();
278             }
279             attrListeners.add(listener);
280             if (attributes == null) {
281                 attributes = new HashPrintJobAttributeSet();
282             }
283             listenedAttributeSets.add(attributes);
284         }
285     }
286 
removePrintJobAttributeListener( PrintJobAttributeListener listener)287     public void removePrintJobAttributeListener(
288                                         PrintJobAttributeListener listener) {
289         synchronized (this) {
290             if (listener == null || attrListeners == null ) {
291                 return;
292             }
293             int index = attrListeners.indexOf(listener);
294             if (index == -1) {
295                 return;
296             } else {
297                 attrListeners.remove(index);
298                 listenedAttributeSets.remove(index);
299                 if (attrListeners.isEmpty()) {
300                     attrListeners = null;
301                     listenedAttributeSets = null;
302                 }
303             }
304         }
305     }
306 
print(Doc doc, PrintRequestAttributeSet attributes)307     public void print(Doc doc, PrintRequestAttributeSet attributes)
308         throws PrintException {
309 
310         synchronized (this) {
311             if (printing) {
312                 throw new PrintException("already printing");
313             } else {
314                 printing = true;
315             }
316         }
317 
318         if ((PrinterIsAcceptingJobs)(service.getAttribute(
319                          PrinterIsAcceptingJobs.class)) ==
320                          PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS) {
321             throw new PrintException("Printer is not accepting job.");
322         }
323 
324         this.doc = doc;
325         /* check if the parameters are valid before doing much processing */
326         DocFlavor flavor = doc.getDocFlavor();
327 
328         Object data;
329 
330         try {
331             data = doc.getPrintData();
332         } catch (IOException e) {
333             notifyEvent(PrintJobEvent.JOB_FAILED);
334             throw new PrintException("can't get print data: " + e.toString());
335         }
336 
337         if (data == null) {
338             throw new PrintException("Null print data.");
339         }
340 
341         if (flavor == null || (!service.isDocFlavorSupported(flavor))) {
342             notifyEvent(PrintJobEvent.JOB_FAILED);
343             throw new PrintJobFlavorException("invalid flavor", flavor);
344         }
345 
346         initializeAttributeSets(doc, attributes);
347 
348         getAttributeValues(flavor);
349 
350         // set up mOptions
351         if ((service instanceof IPPPrintService) &&
352             CUPSPrinter.isCupsRunning()) {
353 
354              IPPPrintService.debug_println(debugPrefix+
355                         "instanceof IPPPrintService");
356 
357              if (mediaName != null) {
358                  CustomMediaSizeName customMedia =
359                      ((IPPPrintService)service).findCustomMedia(mediaName);
360                  if (customMedia != null) {
361                      mOptions = " media="+ customMedia.getChoiceName();
362                  }
363              }
364 
365              if (customTray != null &&
366                  customTray instanceof CustomMediaTray) {
367                  String choice = customTray.getChoiceName();
368                  if (choice != null) {
369                      mOptions += " media="+choice;
370                  }
371              }
372 
373              if (nUp != null) {
374                  mOptions += " number-up="+nUp.getValue();
375              }
376 
377              if (orient != OrientationRequested.PORTRAIT &&
378                  (flavor != null) &&
379                  !flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE)) {
380                  mOptions += " orientation-requested="+orient.getValue();
381              }
382 
383              if (sides != null) {
384                  mOptions += " sides="+sides;
385              }
386 
387         }
388 
389         IPPPrintService.debug_println(debugPrefix+"mOptions "+mOptions);
390         String repClassName = flavor.getRepresentationClassName();
391         String val = flavor.getParameter("charset");
392         String encoding = "us-ascii";
393         if (val != null && !val.equals("")) {
394             encoding = val;
395         }
396 
397         if (flavor.equals(DocFlavor.INPUT_STREAM.GIF) ||
398             flavor.equals(DocFlavor.INPUT_STREAM.JPEG) ||
399             flavor.equals(DocFlavor.INPUT_STREAM.PNG) ||
400             flavor.equals(DocFlavor.BYTE_ARRAY.GIF) ||
401             flavor.equals(DocFlavor.BYTE_ARRAY.JPEG) ||
402             flavor.equals(DocFlavor.BYTE_ARRAY.PNG)) {
403             try {
404                 instream = doc.getStreamForBytes();
405                 if (instream == null) {
406                     notifyEvent(PrintJobEvent.JOB_FAILED);
407                     throw new PrintException("No stream for data");
408                 }
409                 if (!(service instanceof IPPPrintService &&
410                     ((IPPPrintService)service).isIPPSupportedImages(
411                                                 flavor.getMimeType()))) {
412                     printableJob(new ImagePrinter(instream));
413                     ((UnixPrintService)service).wakeNotifier();
414                     return;
415                 }
416             } catch (ClassCastException cce) {
417                 notifyEvent(PrintJobEvent.JOB_FAILED);
418                 throw new PrintException(cce);
419             } catch (IOException ioe) {
420                 notifyEvent(PrintJobEvent.JOB_FAILED);
421                 throw new PrintException(ioe);
422             }
423         } else if (flavor.equals(DocFlavor.URL.GIF) ||
424                    flavor.equals(DocFlavor.URL.JPEG) ||
425                    flavor.equals(DocFlavor.URL.PNG)) {
426             try {
427                 URL url = (URL)data;
428                 if ((service instanceof IPPPrintService) &&
429                     ((IPPPrintService)service).isIPPSupportedImages(
430                                                flavor.getMimeType())) {
431                     instream = url.openStream();
432                 } else {
433                     printableJob(new ImagePrinter(url));
434                     ((UnixPrintService)service).wakeNotifier();
435                     return;
436                 }
437             } catch (ClassCastException cce) {
438                 notifyEvent(PrintJobEvent.JOB_FAILED);
439                 throw new PrintException(cce);
440             } catch (IOException e) {
441                 notifyEvent(PrintJobEvent.JOB_FAILED);
442                 throw new PrintException(e.toString());
443             }
444         } else if (flavor.equals(DocFlavor.CHAR_ARRAY.TEXT_PLAIN) ||
445                    flavor.equals(DocFlavor.READER.TEXT_PLAIN) ||
446                    flavor.equals(DocFlavor.STRING.TEXT_PLAIN)) {
447             try {
448                 reader = doc.getReaderForText();
449                 if (reader == null) {
450                    notifyEvent(PrintJobEvent.JOB_FAILED);
451                    throw new PrintException("No reader for data");
452                 }
453             } catch (IOException ioe) {
454                 notifyEvent(PrintJobEvent.JOB_FAILED);
455                 throw new PrintException(ioe.toString());
456             }
457         } else if (repClassName.equals("[B") ||
458                    repClassName.equals("java.io.InputStream")) {
459             try {
460                 instream = doc.getStreamForBytes();
461                 if (instream == null) {
462                     notifyEvent(PrintJobEvent.JOB_FAILED);
463                     throw new PrintException("No stream for data");
464                 }
465             } catch (IOException ioe) {
466                 notifyEvent(PrintJobEvent.JOB_FAILED);
467                 throw new PrintException(ioe.toString());
468             }
469         } else if  (repClassName.equals("java.net.URL")) {
470             /*
471              * This extracts the data from the URL and passes it the content
472              * directly to the print service as a file.
473              * This is appropriate for the current implementation where lp or
474              * lpr is always used to spool the data. We expect to revise the
475              * implementation to provide more complete IPP support (ie not just
476              * CUPS) and at that time the job will be spooled via IPP
477              * and the URL
478              * itself should be sent to the IPP print service not the content.
479              */
480             URL url = (URL)data;
481             try {
482                 instream = url.openStream();
483             } catch (IOException e) {
484                 notifyEvent(PrintJobEvent.JOB_FAILED);
485                 throw new PrintException(e.toString());
486             }
487         } else if (repClassName.equals("java.awt.print.Pageable")) {
488             try {
489                 pageableJob((Pageable)doc.getPrintData());
490                 if (service instanceof IPPPrintService) {
491                     ((IPPPrintService)service).wakeNotifier();
492                 } else {
493                     ((UnixPrintService)service).wakeNotifier();
494                 }
495                 return;
496             } catch (ClassCastException cce) {
497                 notifyEvent(PrintJobEvent.JOB_FAILED);
498                 throw new PrintException(cce);
499             } catch (IOException ioe) {
500                 notifyEvent(PrintJobEvent.JOB_FAILED);
501                 throw new PrintException(ioe);
502             }
503         } else if (repClassName.equals("java.awt.print.Printable")) {
504             try {
505                 printableJob((Printable)doc.getPrintData());
506                 if (service instanceof IPPPrintService) {
507                     ((IPPPrintService)service).wakeNotifier();
508                 } else {
509                     ((UnixPrintService)service).wakeNotifier();
510                 }
511                 return;
512             } catch (ClassCastException cce) {
513                 notifyEvent(PrintJobEvent.JOB_FAILED);
514                 throw new PrintException(cce);
515             } catch (IOException ioe) {
516                 notifyEvent(PrintJobEvent.JOB_FAILED);
517                 throw new PrintException(ioe);
518             }
519         } else {
520             notifyEvent(PrintJobEvent.JOB_FAILED);
521             throw new PrintException("unrecognized class: "+repClassName);
522         }
523 
524         // now spool the print data.
525         PrinterOpener po = new PrinterOpener();
526         java.security.AccessController.doPrivileged(po);
527         if (po.pex != null) {
528             throw po.pex;
529         }
530         OutputStream output = po.result;
531 
532         /* There are three cases:
533          * 1) Text data from a Reader, just pass through.
534          * 2) Text data from an input stream which we must read using the
535          *    correct encoding
536          * 3) Raw byte data from an InputStream we don't interpret as text,
537          *    just pass through: eg postscript.
538          */
539 
540         BufferedWriter bw = null;
541         if ((instream == null && reader != null)) {
542             BufferedReader br = new BufferedReader(reader);
543             OutputStreamWriter osw = new OutputStreamWriter(output);
544             bw = new BufferedWriter(osw);
545             char []buffer = new char[1024];
546             int cread;
547 
548             try {
549                 while ((cread = br.read(buffer, 0, buffer.length)) >=0) {
550                     bw.write(buffer, 0, cread);
551                 }
552                 br.close();
553                 bw.flush();
554                 bw.close();
555             } catch (IOException e) {
556                 notifyEvent(PrintJobEvent.JOB_FAILED);
557                 throw new PrintException (e);
558             }
559         } else if (instream != null &&
560                    flavor.getMediaType().equalsIgnoreCase("text")) {
561             try {
562 
563                 InputStreamReader isr = new InputStreamReader(instream,
564                                                               encoding);
565                 BufferedReader br = new BufferedReader(isr);
566                 OutputStreamWriter osw = new OutputStreamWriter(output);
567                 bw = new BufferedWriter(osw);
568                 char []buffer = new char[1024];
569                 int cread;
570 
571                 while ((cread = br.read(buffer, 0, buffer.length)) >=0) {
572                     bw.write(buffer, 0, cread);
573                 }
574                 bw.flush();
575             } catch (IOException e) {
576                 notifyEvent(PrintJobEvent.JOB_FAILED);
577                 throw new PrintException (e);
578             } finally {
579                 try {
580                     if (bw != null) {
581                         bw.close();
582                     }
583                 } catch (IOException e) {
584                 }
585             }
586         } else if (instream != null) {
587             BufferedInputStream bin = new BufferedInputStream(instream);
588             BufferedOutputStream bout = new BufferedOutputStream(output);
589             byte[] buffer = new byte[1024];
590             int bread = 0;
591 
592             try {
593                 while ((bread = bin.read(buffer)) >= 0) {
594                     bout.write(buffer, 0, bread);
595                 }
596                 bin.close();
597                 bout.flush();
598                 bout.close();
599             } catch (IOException e) {
600                 notifyEvent(PrintJobEvent.JOB_FAILED);
601                 throw new PrintException (e);
602             }
603         }
604         notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE);
605 
606         if (mDestType == UnixPrintJob.DESTPRINTER) {
607             PrinterSpooler spooler = new PrinterSpooler();
608             java.security.AccessController.doPrivileged(spooler);
609             if (spooler.pex != null) {
610                 throw spooler.pex;
611             }
612         }
613         notifyEvent(PrintJobEvent.NO_MORE_EVENTS);
614         if (service instanceof IPPPrintService) {
615             ((IPPPrintService)service).wakeNotifier();
616         } else {
617             ((UnixPrintService)service).wakeNotifier();
618         }
619     }
620 
printableJob(Printable printable)621     public void printableJob(Printable printable) throws PrintException {
622         try {
623             synchronized(this) {
624                 if (job != null) { // shouldn't happen
625                     throw new PrintException("already printing");
626                 } else {
627                     job = new PSPrinterJob();
628                 }
629             }
630             job.setPrintService(getPrintService());
631             job.setCopies(copies);
632             job.setJobName(jobName);
633             PageFormat pf = new PageFormat();
634             if (mediaSize != null) {
635                 Paper p = new Paper();
636                 p.setSize(mediaSize.getX(MediaSize.INCH)*72.0,
637                           mediaSize.getY(MediaSize.INCH)*72.0);
638                 p.setImageableArea(72.0, 72.0, p.getWidth()-144.0,
639                                    p.getHeight()-144.0);
640                 pf.setPaper(p);
641             }
642             if (orient == OrientationRequested.REVERSE_LANDSCAPE) {
643                 pf.setOrientation(PageFormat.REVERSE_LANDSCAPE);
644             } else if (orient == OrientationRequested.LANDSCAPE) {
645                 pf.setOrientation(PageFormat.LANDSCAPE);
646             }
647             job.setPrintable(printable, pf);
648             job.print(reqAttrSet);
649             notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE);
650             return;
651         } catch (PrinterException pe) {
652             notifyEvent(PrintJobEvent.JOB_FAILED);
653             throw new PrintException(pe);
654         } finally {
655             printReturned = true;
656             notifyEvent(PrintJobEvent.NO_MORE_EVENTS);
657         }
658     }
659 
pageableJob(Pageable pageable)660     public void pageableJob(Pageable pageable) throws PrintException {
661         try {
662             synchronized(this) {
663                 if (job != null) { // shouldn't happen
664                     throw new PrintException("already printing");
665                 } else {
666                     job = new PSPrinterJob();
667                 }
668             }
669             job.setPrintService(getPrintService());
670             job.setCopies(copies);
671             job.setJobName(jobName);
672             job.setPageable(pageable);
673             job.print(reqAttrSet);
674             notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE);
675             return;
676         } catch (PrinterException pe) {
677             notifyEvent(PrintJobEvent.JOB_FAILED);
678             throw new PrintException(pe);
679         } finally {
680             printReturned = true;
681             notifyEvent(PrintJobEvent.NO_MORE_EVENTS);
682         }
683     }
684     /* There's some inefficiency here as the job set is created even though
685      * it may never be requested.
686      */
687     private synchronized void
initializeAttributeSets(Doc doc, PrintRequestAttributeSet reqSet)688         initializeAttributeSets(Doc doc, PrintRequestAttributeSet reqSet) {
689 
690         reqAttrSet = new HashPrintRequestAttributeSet();
691         jobAttrSet = new HashPrintJobAttributeSet();
692 
693         Attribute[] attrs;
694         if (reqSet != null) {
695             reqAttrSet.addAll(reqSet);
696             attrs = reqSet.toArray();
697             for (int i=0; i<attrs.length; i++) {
698                 if (attrs[i] instanceof PrintJobAttribute) {
699                     jobAttrSet.add(attrs[i]);
700                 }
701             }
702         }
703 
704         DocAttributeSet docSet = doc.getAttributes();
705         if (docSet != null) {
706             attrs = docSet.toArray();
707             for (int i=0; i<attrs.length; i++) {
708                 if (attrs[i] instanceof PrintRequestAttribute) {
709                     reqAttrSet.add(attrs[i]);
710                 }
711                 if (attrs[i] instanceof PrintJobAttribute) {
712                     jobAttrSet.add(attrs[i]);
713                 }
714             }
715         }
716 
717         /* add the user name to the job */
718         String userName = "";
719         try {
720           userName = System.getProperty("user.name");
721         } catch (SecurityException se) {
722         }
723 
724         if (userName == null || userName.equals("")) {
725             RequestingUserName ruName =
726                 (RequestingUserName)reqSet.get(RequestingUserName.class);
727             if (ruName != null) {
728                 jobAttrSet.add(
729                     new JobOriginatingUserName(ruName.getValue(),
730                                                ruName.getLocale()));
731             } else {
732                 jobAttrSet.add(new JobOriginatingUserName("", null));
733             }
734         } else {
735             jobAttrSet.add(new JobOriginatingUserName(userName, null));
736         }
737 
738         /* if no job name supplied use doc name (if supplied), if none and
739          * its a URL use that, else finally anything .. */
740         if (jobAttrSet.get(JobName.class) == null) {
741             JobName jobName;
742             if (docSet != null && docSet.get(DocumentName.class) != null) {
743                 DocumentName docName =
744                     (DocumentName)docSet.get(DocumentName.class);
745                 jobName = new JobName(docName.getValue(), docName.getLocale());
746                 jobAttrSet.add(jobName);
747             } else {
748                 String str = "JPS Job:" + doc;
749                 try {
750                     Object printData = doc.getPrintData();
751                     if (printData instanceof URL) {
752                         str = ((URL)(doc.getPrintData())).toString();
753                     }
754                 } catch (IOException e) {
755                 }
756                 jobName = new JobName(str, null);
757                 jobAttrSet.add(jobName);
758             }
759         }
760 
761         jobAttrSet = AttributeSetUtilities.unmodifiableView(jobAttrSet);
762     }
763 
getAttributeValues(DocFlavor flavor)764     private void getAttributeValues(DocFlavor flavor) throws PrintException {
765         Attribute attr;
766         Class category;
767 
768         if (reqAttrSet.get(Fidelity.class) == Fidelity.FIDELITY_TRUE) {
769             fidelity = true;
770         } else {
771             fidelity = false;
772         }
773 
774         Attribute []attrs = reqAttrSet.toArray();
775         for (int i=0; i<attrs.length; i++) {
776             attr = attrs[i];
777             category = attr.getCategory();
778             if (fidelity == true) {
779                 if (!service.isAttributeCategorySupported(category)) {
780                     notifyEvent(PrintJobEvent.JOB_FAILED);
781                     throw new PrintJobAttributeException(
782                         "unsupported category: " + category, category, null);
783                 } else if
784                     (!service.isAttributeValueSupported(attr, flavor, null)) {
785                     notifyEvent(PrintJobEvent.JOB_FAILED);
786                     throw new PrintJobAttributeException(
787                         "unsupported attribute: " + attr, null, attr);
788                 }
789             }
790             if (category == Destination.class) {
791                 URI uri = ((Destination)attr).getURI();
792                 if (!"file".equals(uri.getScheme())) {
793                     notifyEvent(PrintJobEvent.JOB_FAILED);
794                     throw new PrintException("Not a file: URI");
795                 } else {
796                     try {
797                         mDestType = DESTFILE;
798                         mDestination = (new File(uri)).getPath();
799                     } catch (Exception e) {
800                         throw new PrintException(e);
801                     }
802                     // check write access
803                     SecurityManager security = System.getSecurityManager();
804                     if (security != null) {
805                       try {
806                         security.checkWrite(mDestination);
807                       } catch (SecurityException se) {
808                         notifyEvent(PrintJobEvent.JOB_FAILED);
809                         throw new PrintException(se);
810                       }
811                     }
812                 }
813             } else if (category == JobSheets.class) {
814                 if ((JobSheets)attr == JobSheets.NONE) {
815                    mNoJobSheet = true;
816                 }
817             } else if (category == JobName.class) {
818                 jobName = ((JobName)attr).getValue();
819             } else if (category == Copies.class) {
820                 copies = ((Copies)attr).getValue();
821             } else if (category == Media.class) {
822                 if (attr instanceof MediaSizeName) {
823                     mediaName = (MediaSizeName)attr;
824                     IPPPrintService.debug_println(debugPrefix+
825                                                   "mediaName "+mediaName);
826                 if (!service.isAttributeValueSupported(attr, null, null)) {
827                     mediaSize = MediaSize.getMediaSizeForName(mediaName);
828                 }
829               } else if (attr instanceof CustomMediaTray) {
830                   customTray = (CustomMediaTray)attr;
831               }
832             } else if (category == OrientationRequested.class) {
833                 orient = (OrientationRequested)attr;
834             } else if (category == NumberUp.class) {
835                 nUp = (NumberUp)attr;
836             } else if (category == Sides.class) {
837                 sides = (Sides)attr;
838             }
839         }
840     }
841 
printExecCmd(String printer, String options, boolean noJobSheet, String banner, int copies, String spoolFile)842     private String[] printExecCmd(String printer, String options,
843                                  boolean noJobSheet,
844                                  String banner, int copies, String spoolFile) {
845         int PRINTER = 0x1;
846         int OPTIONS = 0x2;
847         int BANNER  = 0x4;
848         int COPIES  = 0x8;
849         int NOSHEET  = 0x10;
850         int pFlags = 0;
851         String execCmd[];
852         int ncomps = 2; // minimum number of print args
853         int n = 0;
854 
855         // conveniently "lp" is the default destination for both lp and lpr.
856         if (printer != null && !printer.equals("") && !printer.equals("lp")) {
857             pFlags |= PRINTER;
858             ncomps+=1;
859         }
860         if (options != null && !options.equals("")) {
861             pFlags |= OPTIONS;
862             ncomps+=1;
863         }
864         if (banner != null && !banner.equals("")) {
865             pFlags |= BANNER;
866             ncomps+=1;
867         }
868         if (copies > 1) {
869             pFlags |= COPIES;
870             ncomps+=1;
871         }
872         if (noJobSheet) {
873             pFlags |= NOSHEET;
874             ncomps+=1;
875         }
876         if (PrintServiceLookupProvider.osname.equals("SunOS")) {
877             ncomps+=1; // lp uses 1 more arg than lpr (make a copy)
878             execCmd = new String[ncomps];
879             execCmd[n++] = "/usr/bin/lp";
880             execCmd[n++] = "-c";           // make a copy of the spool file
881             if ((pFlags & PRINTER) != 0) {
882                 execCmd[n++] = "-d" + printer;
883             }
884             if ((pFlags & BANNER) != 0) {
885                 String quoteChar = "\"";
886                 execCmd[n++] = "-t "  + quoteChar+banner+quoteChar;
887             }
888             if ((pFlags & COPIES) != 0) {
889                 execCmd[n++] = "-n " + copies;
890             }
891             if ((pFlags & NOSHEET) != 0) {
892                 execCmd[n++] = "-o nobanner";
893             }
894             if ((pFlags & OPTIONS) != 0) {
895                 execCmd[n++] = "-o " + options;
896             }
897         } else {
898             execCmd = new String[ncomps];
899             execCmd[n++] = "/usr/bin/lpr";
900             if ((pFlags & PRINTER) != 0) {
901                 execCmd[n++] = "-P" + printer;
902             }
903             if ((pFlags & BANNER) != 0) {
904                 execCmd[n++] = "-J "  + banner;
905             }
906             if ((pFlags & COPIES) != 0) {
907                 execCmd[n++] = "-#" + copies;
908             }
909             if ((pFlags & NOSHEET) != 0) {
910                 execCmd[n++] = "-h";
911             }
912             if ((pFlags & OPTIONS) != 0) {
913                 execCmd[n++] = "-o" + options;
914             }
915         }
916         execCmd[n++] = spoolFile;
917         if (IPPPrintService.debugPrint) {
918             System.out.println("UnixPrintJob>> execCmd");
919             for (int i=0; i<execCmd.length; i++) {
920                 System.out.print(" "+execCmd[i]);
921             }
922             System.out.println();
923         }
924         return execCmd;
925     }
926 
927     private static int DESTPRINTER = 1;
928     private static int DESTFILE = 2;
929     private int mDestType = DESTPRINTER;
930 
931     private File spoolFile;
932     private String mDestination, mOptions="";
933     private boolean mNoJobSheet = false;
934 
935     // Inner class to run "privileged" to open the printer output stream.
936 
937     private class PrinterOpener implements java.security.PrivilegedAction {
938         PrintException pex;
939         OutputStream result;
940 
run()941         public Object run() {
942             try {
943                 if (mDestType == UnixPrintJob.DESTFILE) {
944                     spoolFile = new File(mDestination);
945                 } else {
946                     /* Write to a temporary file which will be spooled to
947                      * the printer then deleted. In the case that the file
948                      * is not removed for some reason, request that it is
949                      * removed when the VM exits.
950                      */
951                     spoolFile = Files.createTempFile("javaprint", "").toFile();
952                     spoolFile.deleteOnExit();
953                 }
954                 result = new FileOutputStream(spoolFile);
955                 return result;
956             } catch (IOException ex) {
957                 // If there is an IOError we subvert it to a PrinterException.
958                 notifyEvent(PrintJobEvent.JOB_FAILED);
959                 pex = new PrintException(ex);
960             }
961             return null;
962         }
963     }
964 
965     // Inner class to run "privileged" to invoke the system print command
966 
967     private class PrinterSpooler implements java.security.PrivilegedAction {
968         PrintException pex;
969 
handleProcessFailure(final Process failedProcess, final String[] execCmd, final int result)970         private void handleProcessFailure(final Process failedProcess,
971                 final String[] execCmd, final int result) throws IOException {
972             try (StringWriter sw = new StringWriter();
973                     PrintWriter pw = new PrintWriter(sw)) {
974                 pw.append("error=").append(Integer.toString(result));
975                 pw.append(" running:");
976                 for (String arg: execCmd) {
977                     pw.append(" '").append(arg).append("'");
978                 }
979                 try (InputStream is = failedProcess.getErrorStream();
980                         InputStreamReader isr = new InputStreamReader(is);
981                         BufferedReader br = new BufferedReader(isr)) {
982                     while (br.ready()) {
983                         pw.println();
984                         pw.append("\t\t").append(br.readLine());
985                     }
986                 } finally {
987                     pw.flush();
988                     throw new IOException(sw.toString());
989                 }
990             }
991         }
992 
run()993         public Object run() {
994             if (spoolFile == null || !spoolFile.exists()) {
995                pex = new PrintException("No spool file");
996                notifyEvent(PrintJobEvent.JOB_FAILED);
997                return null;
998             }
999             try {
1000                 /**
1001                  * Spool to the printer.
1002                  */
1003                 String fileName = spoolFile.getAbsolutePath();
1004                 String execCmd[] = printExecCmd(mDestination, mOptions,
1005                                mNoJobSheet, jobName, copies, fileName);
1006 
1007                 Process process = Runtime.getRuntime().exec(execCmd);
1008                 process.waitFor();
1009                 final int result = process.exitValue();
1010                 if (0 != result) {
1011                     handleProcessFailure(process, execCmd, result);
1012                 }
1013                 notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE);
1014             } catch (IOException ex) {
1015                 notifyEvent(PrintJobEvent.JOB_FAILED);
1016                 // REMIND : 2d printing throws PrinterException
1017                 pex = new PrintException(ex);
1018             } catch (InterruptedException ie) {
1019                 notifyEvent(PrintJobEvent.JOB_FAILED);
1020                 pex = new PrintException(ie);
1021             } finally {
1022                 spoolFile.delete();
1023                 notifyEvent(PrintJobEvent.NO_MORE_EVENTS);
1024             }
1025             return null;
1026         }
1027     }
1028 
cancel()1029     public void cancel() throws PrintException {
1030         synchronized (this) {
1031             if (!printing) {
1032                 throw new PrintException("Job is not yet submitted.");
1033             } else if (job != null && !printReturned) {
1034                 job.cancel();
1035                 notifyEvent(PrintJobEvent.JOB_CANCELED);
1036                 return;
1037             } else {
1038                 throw new PrintException("Job could not be cancelled.");
1039             }
1040         }
1041     }
1042 }
1043