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