1 /* 2 * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.print; 27 28 import java.net.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 122 UnixPrintJob(PrintService service) { 123 this.service = service; 124 mDestination = service.getName(); 125 if (UnixPrintServiceLookup.isMac()) { 126 mDestination = ((IPPPrintService)service).getDest(); 127 } 128 mDestType = UnixPrintJob.DESTPRINTER; 129 } 130 131 public PrintService getPrintService() { 132 return service; 133 } 134 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 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 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 */ 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 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 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 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 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 ((service.getAttribute(PrinterIsAcceptingJobs.class)) == 319 PrinterIsAcceptingJobs.NOT_ACCEPTING_JOBS) { 320 throw new PrintException("Printer is not accepting job."); 321 } 322 323 this.doc = doc; 324 /* check if the parameters are valid before doing much processing */ 325 DocFlavor flavor = doc.getDocFlavor(); 326 327 Object data; 328 329 try { 330 data = doc.getPrintData(); 331 } catch (IOException e) { 332 notifyEvent(PrintJobEvent.JOB_FAILED); 333 throw new PrintException("can't get print data: " + e.toString()); 334 } 335 336 if (data == null) { 337 throw new PrintException("Null print data."); 338 } 339 340 if (flavor == null || (!service.isDocFlavorSupported(flavor))) { 341 notifyEvent(PrintJobEvent.JOB_FAILED); 342 throw new PrintJobFlavorException("invalid flavor", flavor); 343 } 344 345 initializeAttributeSets(doc, attributes); 346 347 getAttributeValues(flavor); 348 349 // set up mOptions 350 if ((service instanceof IPPPrintService) && 351 CUPSPrinter.isCupsRunning()) { 352 353 IPPPrintService.debug_println(debugPrefix+ 354 "instanceof IPPPrintService"); 355 356 if (mediaName != null) { 357 CustomMediaSizeName customMedia = 358 ((IPPPrintService)service).findCustomMedia(mediaName); 359 if (customMedia != null) { 360 mOptions = " media="+ customMedia.getChoiceName(); 361 } 362 } 363 364 if (customTray != null && 365 customTray instanceof CustomMediaTray) { 366 String choice = customTray.getChoiceName(); 367 if (choice != null) { 368 mOptions += " media="+choice; 369 } 370 } 371 372 if (nUp != null) { 373 mOptions += " number-up="+nUp.getValue(); 374 } 375 376 if (orient != OrientationRequested.PORTRAIT && 377 (flavor != null) && 378 !flavor.equals(DocFlavor.SERVICE_FORMATTED.PAGEABLE)) { 379 mOptions += " orientation-requested="+orient.getValue(); 380 } 381 382 if (sides != null) { 383 mOptions += " sides="+sides; 384 } 385 386 } 387 388 IPPPrintService.debug_println(debugPrefix+"mOptions "+mOptions); 389 String repClassName = flavor.getRepresentationClassName(); 390 String val = flavor.getParameter("charset"); 391 String encoding = "us-ascii"; 392 if (val != null && !val.equals("")) { 393 encoding = val; 394 } 395 396 if (flavor.equals(DocFlavor.INPUT_STREAM.GIF) || 397 flavor.equals(DocFlavor.INPUT_STREAM.JPEG) || 398 flavor.equals(DocFlavor.INPUT_STREAM.PNG) || 399 flavor.equals(DocFlavor.BYTE_ARRAY.GIF) || 400 flavor.equals(DocFlavor.BYTE_ARRAY.JPEG) || 401 flavor.equals(DocFlavor.BYTE_ARRAY.PNG)) { 402 try { 403 instream = doc.getStreamForBytes(); 404 if (instream == null) { 405 notifyEvent(PrintJobEvent.JOB_FAILED); 406 throw new PrintException("No stream for data"); 407 } 408 if (!(service instanceof IPPPrintService && 409 ((IPPPrintService)service).isIPPSupportedImages( 410 flavor.getMimeType()))) { 411 printableJob(new ImagePrinter(instream)); 412 ((UnixPrintService)service).wakeNotifier(); 413 return; 414 } 415 } catch (ClassCastException cce) { 416 notifyEvent(PrintJobEvent.JOB_FAILED); 417 throw new PrintException(cce); 418 } catch (IOException ioe) { 419 notifyEvent(PrintJobEvent.JOB_FAILED); 420 throw new PrintException(ioe); 421 } 422 } else if (flavor.equals(DocFlavor.URL.GIF) || 423 flavor.equals(DocFlavor.URL.JPEG) || 424 flavor.equals(DocFlavor.URL.PNG)) { 425 try { 426 URL url = (URL)data; 427 if ((service instanceof IPPPrintService) && 428 ((IPPPrintService)service).isIPPSupportedImages( 429 flavor.getMimeType())) { 430 instream = url.openStream(); 431 } else { 432 printableJob(new ImagePrinter(url)); 433 ((UnixPrintService)service).wakeNotifier(); 434 return; 435 } 436 } catch (ClassCastException cce) { 437 notifyEvent(PrintJobEvent.JOB_FAILED); 438 throw new PrintException(cce); 439 } catch (IOException e) { 440 notifyEvent(PrintJobEvent.JOB_FAILED); 441 throw new PrintException(e.toString()); 442 } 443 } else if (flavor.equals(DocFlavor.CHAR_ARRAY.TEXT_PLAIN) || 444 flavor.equals(DocFlavor.READER.TEXT_PLAIN) || 445 flavor.equals(DocFlavor.STRING.TEXT_PLAIN)) { 446 try { 447 reader = doc.getReaderForText(); 448 if (reader == null) { 449 notifyEvent(PrintJobEvent.JOB_FAILED); 450 throw new PrintException("No reader for data"); 451 } 452 } catch (IOException ioe) { 453 notifyEvent(PrintJobEvent.JOB_FAILED); 454 throw new PrintException(ioe.toString()); 455 } 456 } else if (repClassName.equals("[B") || 457 repClassName.equals("java.io.InputStream")) { 458 try { 459 instream = doc.getStreamForBytes(); 460 if (instream == null) { 461 notifyEvent(PrintJobEvent.JOB_FAILED); 462 throw new PrintException("No stream for data"); 463 } 464 } catch (IOException ioe) { 465 notifyEvent(PrintJobEvent.JOB_FAILED); 466 throw new PrintException(ioe.toString()); 467 } 468 } else if (repClassName.equals("java.net.URL")) { 469 /* 470 * This extracts the data from the URL and passes it the content 471 * directly to the print service as a file. 472 * This is appropriate for the current implementation where lp or 473 * lpr is always used to spool the data. We expect to revise the 474 * implementation to provide more complete IPP support (ie not just 475 * CUPS) and at that time the job will be spooled via IPP 476 * and the URL 477 * itself should be sent to the IPP print service not the content. 478 */ 479 URL url = (URL)data; 480 try { 481 instream = url.openStream(); 482 } catch (IOException e) { 483 notifyEvent(PrintJobEvent.JOB_FAILED); 484 throw new PrintException(e.toString()); 485 } 486 } else if (repClassName.equals("java.awt.print.Pageable")) { 487 try { 488 pageableJob((Pageable)doc.getPrintData()); 489 if (service instanceof IPPPrintService) { 490 ((IPPPrintService)service).wakeNotifier(); 491 } else { 492 ((UnixPrintService)service).wakeNotifier(); 493 } 494 return; 495 } catch (ClassCastException cce) { 496 notifyEvent(PrintJobEvent.JOB_FAILED); 497 throw new PrintException(cce); 498 } catch (IOException ioe) { 499 notifyEvent(PrintJobEvent.JOB_FAILED); 500 throw new PrintException(ioe); 501 } 502 } else if (repClassName.equals("java.awt.print.Printable")) { 503 try { 504 printableJob((Printable)doc.getPrintData()); 505 if (service instanceof IPPPrintService) { 506 ((IPPPrintService)service).wakeNotifier(); 507 } else { 508 ((UnixPrintService)service).wakeNotifier(); 509 } 510 return; 511 } catch (ClassCastException cce) { 512 notifyEvent(PrintJobEvent.JOB_FAILED); 513 throw new PrintException(cce); 514 } catch (IOException ioe) { 515 notifyEvent(PrintJobEvent.JOB_FAILED); 516 throw new PrintException(ioe); 517 } 518 } else { 519 notifyEvent(PrintJobEvent.JOB_FAILED); 520 throw new PrintException("unrecognized class: "+repClassName); 521 } 522 523 // now spool the print data. 524 PrinterOpener po = new PrinterOpener(); 525 java.security.AccessController.doPrivileged(po); 526 if (po.pex != null) { 527 throw po.pex; 528 } 529 OutputStream output = po.result; 530 531 /* There are three cases: 532 * 1) Text data from a Reader, just pass through. 533 * 2) Text data from an input stream which we must read using the 534 * correct encoding 535 * 3) Raw byte data from an InputStream we don't interpret as text, 536 * just pass through: eg postscript. 537 */ 538 539 BufferedWriter bw = null; 540 if ((instream == null && reader != null)) { 541 BufferedReader br = new BufferedReader(reader); 542 OutputStreamWriter osw = new OutputStreamWriter(output); 543 bw = new BufferedWriter(osw); 544 char []buffer = new char[1024]; 545 int cread; 546 547 try { 548 while ((cread = br.read(buffer, 0, buffer.length)) >=0) { 549 bw.write(buffer, 0, cread); 550 } 551 br.close(); 552 bw.flush(); 553 bw.close(); 554 } catch (IOException e) { 555 notifyEvent(PrintJobEvent.JOB_FAILED); 556 throw new PrintException (e); 557 } 558 } else if (instream != null && 559 flavor.getMediaType().equalsIgnoreCase("text")) { 560 try { 561 562 InputStreamReader isr = new InputStreamReader(instream, 563 encoding); 564 BufferedReader br = new BufferedReader(isr); 565 OutputStreamWriter osw = new OutputStreamWriter(output); 566 bw = new BufferedWriter(osw); 567 char []buffer = new char[1024]; 568 int cread; 569 570 while ((cread = br.read(buffer, 0, buffer.length)) >=0) { 571 bw.write(buffer, 0, cread); 572 } 573 bw.flush(); 574 } catch (IOException e) { 575 notifyEvent(PrintJobEvent.JOB_FAILED); 576 throw new PrintException (e); 577 } finally { 578 try { 579 if (bw != null) { 580 bw.close(); 581 } 582 } catch (IOException e) { 583 } 584 } 585 } else if (instream != null) { 586 BufferedInputStream bin = new BufferedInputStream(instream); 587 BufferedOutputStream bout = new BufferedOutputStream(output); 588 byte[] buffer = new byte[1024]; 589 int bread = 0; 590 591 try { 592 while ((bread = bin.read(buffer)) >= 0) { 593 bout.write(buffer, 0, bread); 594 } 595 bin.close(); 596 bout.flush(); 597 bout.close(); 598 } catch (IOException e) { 599 notifyEvent(PrintJobEvent.JOB_FAILED); 600 throw new PrintException (e); 601 } 602 } 603 notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE); 604 605 if (mDestType == UnixPrintJob.DESTPRINTER) { 606 PrinterSpooler spooler = new PrinterSpooler(); 607 java.security.AccessController.doPrivileged(spooler); 608 if (spooler.pex != null) { 609 throw spooler.pex; 610 } 611 } 612 notifyEvent(PrintJobEvent.NO_MORE_EVENTS); 613 if (service instanceof IPPPrintService) { 614 ((IPPPrintService)service).wakeNotifier(); 615 } else { 616 ((UnixPrintService)service).wakeNotifier(); 617 } 618 } 619 620 public void printableJob(Printable printable) throws PrintException { 621 try { 622 synchronized(this) { 623 if (job != null) { // shouldn't happen 624 throw new PrintException("already printing"); 625 } else { 626 job = new PSPrinterJob(); 627 } 628 } 629 job.setPrintService(getPrintService()); 630 job.setCopies(copies); 631 job.setJobName(jobName); 632 PageFormat pf = new PageFormat(); 633 if (mediaSize != null) { 634 Paper p = new Paper(); 635 p.setSize(mediaSize.getX(MediaSize.INCH)*72.0, 636 mediaSize.getY(MediaSize.INCH)*72.0); 637 p.setImageableArea(72.0, 72.0, p.getWidth()-144.0, 638 p.getHeight()-144.0); 639 pf.setPaper(p); 640 } 641 if (orient == OrientationRequested.REVERSE_LANDSCAPE) { 642 pf.setOrientation(PageFormat.REVERSE_LANDSCAPE); 643 } else if (orient == OrientationRequested.LANDSCAPE) { 644 pf.setOrientation(PageFormat.LANDSCAPE); 645 } 646 job.setPrintable(printable, pf); 647 job.print(reqAttrSet); 648 notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE); 649 return; 650 } catch (PrinterException pe) { 651 notifyEvent(PrintJobEvent.JOB_FAILED); 652 throw new PrintException(pe); 653 } finally { 654 printReturned = true; 655 notifyEvent(PrintJobEvent.NO_MORE_EVENTS); 656 } 657 } 658 659 public void pageableJob(Pageable pageable) throws PrintException { 660 try { 661 synchronized(this) { 662 if (job != null) { // shouldn't happen 663 throw new PrintException("already printing"); 664 } else { 665 job = new PSPrinterJob(); 666 } 667 } 668 job.setPrintService(getPrintService()); 669 job.setCopies(copies); 670 job.setJobName(jobName); 671 job.setPageable(pageable); 672 job.print(reqAttrSet); 673 notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE); 674 return; 675 } catch (PrinterException pe) { 676 notifyEvent(PrintJobEvent.JOB_FAILED); 677 throw new PrintException(pe); 678 } finally { 679 printReturned = true; 680 notifyEvent(PrintJobEvent.NO_MORE_EVENTS); 681 } 682 } 683 /* There's some inefficiency here as the job set is created even though 684 * it may never be requested. 685 */ 686 private synchronized void 687 initializeAttributeSets(Doc doc, PrintRequestAttributeSet reqSet) { 688 689 reqAttrSet = new HashPrintRequestAttributeSet(); 690 jobAttrSet = new HashPrintJobAttributeSet(); 691 692 Attribute[] attrs; 693 if (reqSet != null) { 694 reqAttrSet.addAll(reqSet); 695 attrs = reqSet.toArray(); 696 for (int i=0; i<attrs.length; i++) { 697 if (attrs[i] instanceof PrintJobAttribute) { 698 jobAttrSet.add(attrs[i]); 699 } 700 } 701 } 702 703 DocAttributeSet docSet = doc.getAttributes(); 704 if (docSet != null) { 705 attrs = docSet.toArray(); 706 for (int i=0; i<attrs.length; i++) { 707 if (attrs[i] instanceof PrintRequestAttribute) { 708 reqAttrSet.add(attrs[i]); 709 } 710 if (attrs[i] instanceof PrintJobAttribute) { 711 jobAttrSet.add(attrs[i]); 712 } 713 } 714 } 715 716 /* add the user name to the job */ 717 String userName = ""; 718 try { 719 userName = System.getProperty("user.name"); 720 } catch (SecurityException se) { 721 } 722 723 if (userName == null || userName.equals("")) { 724 RequestingUserName ruName = 725 (RequestingUserName)reqSet.get(RequestingUserName.class); 726 if (ruName != null) { 727 jobAttrSet.add( 728 new JobOriginatingUserName(ruName.getValue(), 729 ruName.getLocale())); 730 } else { 731 jobAttrSet.add(new JobOriginatingUserName("", null)); 732 } 733 } else { 734 jobAttrSet.add(new JobOriginatingUserName(userName, null)); 735 } 736 737 /* if no job name supplied use doc name (if supplied), if none and 738 * its a URL use that, else finally anything .. */ 739 if (jobAttrSet.get(JobName.class) == null) { 740 JobName jobName; 741 if (docSet != null && docSet.get(DocumentName.class) != null) { 742 DocumentName docName = 743 (DocumentName)docSet.get(DocumentName.class); 744 jobName = new JobName(docName.getValue(), docName.getLocale()); 745 jobAttrSet.add(jobName); 746 } else { 747 String str = "JPS Job:" + doc; 748 try { 749 Object printData = doc.getPrintData(); 750 if (printData instanceof URL) { 751 str = ((URL)(doc.getPrintData())).toString(); 752 } 753 } catch (IOException e) { 754 } 755 jobName = new JobName(str, null); 756 jobAttrSet.add(jobName); 757 } 758 } 759 760 jobAttrSet = AttributeSetUtilities.unmodifiableView(jobAttrSet); 761 } 762 763 private void getAttributeValues(DocFlavor flavor) throws PrintException { 764 Attribute attr; 765 Class category; 766 767 if (reqAttrSet.get(Fidelity.class) == Fidelity.FIDELITY_TRUE) { 768 fidelity = true; 769 } else { 770 fidelity = false; 771 } 772 773 Attribute []attrs = reqAttrSet.toArray(); 774 for (int i=0; i<attrs.length; i++) { 775 attr = attrs[i]; 776 category = attr.getCategory(); 777 if (fidelity == true) { 778 if (!service.isAttributeCategorySupported(category)) { 779 notifyEvent(PrintJobEvent.JOB_FAILED); 780 throw new PrintJobAttributeException( 781 "unsupported category: " + category, category, null); 782 } else if 783 (!service.isAttributeValueSupported(attr, flavor, null)) { 784 notifyEvent(PrintJobEvent.JOB_FAILED); 785 throw new PrintJobAttributeException( 786 "unsupported attribute: " + attr, null, attr); 787 } 788 } 789 if (category == Destination.class) { 790 URI uri = ((Destination)attr).getURI(); 791 if (!"file".equals(uri.getScheme())) { 792 notifyEvent(PrintJobEvent.JOB_FAILED); 793 throw new PrintException("Not a file: URI"); 794 } else { 795 try { 796 mDestType = DESTFILE; 797 mDestination = (new File(uri)).getPath(); 798 } catch (Exception e) { 799 throw new PrintException(e); 800 } 801 // check write access 802 SecurityManager security = System.getSecurityManager(); 803 if (security != null) { 804 try { 805 security.checkWrite(mDestination); 806 } catch (SecurityException se) { 807 notifyEvent(PrintJobEvent.JOB_FAILED); 808 throw new PrintException(se); 809 } 810 } 811 } 812 } else if (category == JobSheets.class) { 813 if ((JobSheets)attr == JobSheets.NONE) { 814 mNoJobSheet = true; 815 } 816 } else if (category == JobName.class) { 817 jobName = ((JobName)attr).getValue(); 818 } else if (category == Copies.class) { 819 copies = ((Copies)attr).getValue(); 820 } else if (category == Media.class) { 821 if (attr instanceof MediaSizeName) { 822 mediaName = (MediaSizeName)attr; 823 IPPPrintService.debug_println(debugPrefix+ 824 "mediaName "+mediaName); 825 if (!service.isAttributeValueSupported(attr, null, null)) { 826 mediaSize = MediaSize.getMediaSizeForName(mediaName); 827 } 828 } else if (attr instanceof CustomMediaTray) { 829 customTray = (CustomMediaTray)attr; 830 } 831 } else if (category == OrientationRequested.class) { 832 orient = (OrientationRequested)attr; 833 } else if (category == NumberUp.class) { 834 nUp = (NumberUp)attr; 835 } else if (category == Sides.class) { 836 sides = (Sides)attr; 837 } 838 } 839 } 840 841 private String[] printExecCmd(String printer, String options, 842 boolean noJobSheet, 843 String banner, int copies, String spoolFile) { 844 int PRINTER = 0x1; 845 int OPTIONS = 0x2; 846 int BANNER = 0x4; 847 int COPIES = 0x8; 848 int NOSHEET = 0x10; 849 int pFlags = 0; 850 String execCmd[]; 851 int ncomps = 2; // minimum number of print args 852 int n = 0; 853 854 // conveniently "lp" is the default destination for both lp and lpr. 855 if (printer != null && !printer.equals("") && !printer.equals("lp")) { 856 pFlags |= PRINTER; 857 ncomps+=1; 858 } 859 if (options != null && !options.equals("")) { 860 pFlags |= OPTIONS; 861 ncomps+=1; 862 } 863 if (banner != null && !banner.equals("")) { 864 pFlags |= BANNER; 865 ncomps+=1; 866 } 867 if (copies > 1) { 868 pFlags |= COPIES; 869 ncomps+=1; 870 } 871 if (noJobSheet) { 872 pFlags |= NOSHEET; 873 ncomps+=1; 874 } 875 if (UnixPrintServiceLookup.osname.equals("SunOS")) { 876 ncomps+=1; // lp uses 1 more arg than lpr (make a copy) 877 execCmd = new String[ncomps]; 878 execCmd[n++] = "/usr/bin/lp"; 879 execCmd[n++] = "-c"; // make a copy of the spool file 880 if ((pFlags & PRINTER) != 0) { 881 execCmd[n++] = "-d" + printer; 882 } 883 if ((pFlags & BANNER) != 0) { 884 String quoteChar = "\""; 885 execCmd[n++] = "-t " + quoteChar+banner+quoteChar; 886 } 887 if ((pFlags & COPIES) != 0) { 888 execCmd[n++] = "-n " + copies; 889 } 890 if ((pFlags & NOSHEET) != 0) { 891 execCmd[n++] = "-o nobanner"; 892 } 893 if ((pFlags & OPTIONS) != 0) { 894 execCmd[n++] = "-o " + options; 895 } 896 } else { 897 execCmd = new String[ncomps]; 898 execCmd[n++] = "/usr/bin/lpr"; 899 if ((pFlags & PRINTER) != 0) { 900 execCmd[n++] = "-P" + printer; 901 } 902 if ((pFlags & BANNER) != 0) { 903 execCmd[n++] = "-J " + banner; 904 } 905 if ((pFlags & COPIES) != 0) { 906 execCmd[n++] = "-#" + copies; 907 } 908 if ((pFlags & NOSHEET) != 0) { 909 execCmd[n++] = "-h"; 910 } 911 if ((pFlags & OPTIONS) != 0) { 912 execCmd[n++] = "-o" + options; 913 } 914 } 915 execCmd[n++] = spoolFile; 916 if (IPPPrintService.debugPrint) { 917 System.out.println("UnixPrintJob>> execCmd"); 918 for (int i=0; i<execCmd.length; i++) { 919 System.out.print(" "+execCmd[i]); 920 } 921 System.out.println(); 922 } 923 return execCmd; 924 } 925 926 private static int DESTPRINTER = 1; 927 private static int DESTFILE = 2; 928 private int mDestType = DESTPRINTER; 929 930 private File spoolFile; 931 private String mDestination, mOptions=""; 932 private boolean mNoJobSheet = false; 933 934 // Inner class to run "privileged" to open the printer output stream. 935 936 private class PrinterOpener implements java.security.PrivilegedAction { 937 PrintException pex; 938 OutputStream result; 939 940 public Object run() { 941 try { 942 if (mDestType == UnixPrintJob.DESTFILE) { 943 spoolFile = new File(mDestination); 944 } else { 945 /* Write to a temporary file which will be spooled to 946 * the printer then deleted. In the case that the file 947 * is not removed for some reason, request that it is 948 * removed when the VM exits. 949 */ 950 spoolFile = Files.createTempFile("javaprint", "").toFile(); 951 spoolFile.deleteOnExit(); 952 } 953 result = new FileOutputStream(spoolFile); 954 return result; 955 } catch (IOException ex) { 956 // If there is an IOError we subvert it to a PrinterException. 957 notifyEvent(PrintJobEvent.JOB_FAILED); 958 pex = new PrintException(ex); 959 } 960 return null; 961 } 962 } 963 964 // Inner class to run "privileged" to invoke the system print command 965 966 private class PrinterSpooler implements java.security.PrivilegedAction { 967 PrintException pex; 968 969 private void handleProcessFailure(final Process failedProcess, 970 final String[] execCmd, final int result) throws IOException { 971 try (StringWriter sw = new StringWriter(); 972 PrintWriter pw = new PrintWriter(sw)) { 973 pw.append("error=").append(Integer.toString(result)); 974 pw.append(" running:"); 975 for (String arg: execCmd) { 976 pw.append(" '").append(arg).append("'"); 977 } 978 try (InputStream is = failedProcess.getErrorStream(); 979 InputStreamReader isr = new InputStreamReader(is); 980 BufferedReader br = new BufferedReader(isr)) { 981 while (br.ready()) { 982 pw.println(); 983 pw.append("\t\t").append(br.readLine()); 984 } 985 } finally { 986 pw.flush(); 987 throw new IOException(sw.toString()); 988 } 989 } 990 } 991 992 public Object run() { 993 if (spoolFile == null || !spoolFile.exists()) { 994 pex = new PrintException("No spool file"); 995 notifyEvent(PrintJobEvent.JOB_FAILED); 996 return null; 997 } 998 try { 999 /** 1000 * Spool to the printer. 1001 */ 1002 String fileName = spoolFile.getAbsolutePath(); 1003 String execCmd[] = printExecCmd(mDestination, mOptions, 1004 mNoJobSheet, jobName, copies, fileName); 1005 1006 Process process = Runtime.getRuntime().exec(execCmd); 1007 process.waitFor(); 1008 final int result = process.exitValue(); 1009 if (0 != result) { 1010 handleProcessFailure(process, execCmd, result); 1011 } 1012 notifyEvent(PrintJobEvent.DATA_TRANSFER_COMPLETE); 1013 } catch (IOException ex) { 1014 notifyEvent(PrintJobEvent.JOB_FAILED); 1015 // REMIND : 2d printing throws PrinterException 1016 pex = new PrintException(ex); 1017 } catch (InterruptedException ie) { 1018 notifyEvent(PrintJobEvent.JOB_FAILED); 1019 pex = new PrintException(ie); 1020 } finally { 1021 spoolFile.delete(); 1022 notifyEvent(PrintJobEvent.NO_MORE_EVENTS); 1023 } 1024 return null; 1025 } 1026 } 1027 1028 public void cancel() throws PrintException { 1029 synchronized (this) { 1030 if (!printing) { 1031 throw new PrintException("Job is not yet submitted."); 1032 } else if (job != null && !printReturned) { 1033 job.cancel(); 1034 notifyEvent(PrintJobEvent.JOB_CANCELED); 1035 return; 1036 } else { 1037 throw new PrintException("Job could not be cancelled."); 1038 } 1039 } 1040 } 1041 }