1 /* 2 * Copyright (c) 1997, 2017, 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 com.sun.xml.internal.messaging.saaj.soap; 27 28 import com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl; 29 30 import com.sun.xml.internal.messaging.saaj.packaging.mime.util.ASCIIUtility; 31 32 import com.sun.xml.internal.messaging.saaj.packaging.mime.Header; 33 import com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimePartDataSource; 34 import com.sun.xml.internal.messaging.saaj.packaging.mime.internet.InternetHeaders; 35 import com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimeBodyPart; 36 import com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimeUtility; 37 import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream; 38 import com.sun.xml.internal.messaging.saaj.util.LogDomainConstants; 39 40 import java.io.IOException; 41 import java.io.InputStream; 42 import java.io.ByteArrayOutputStream; 43 import java.io.ByteArrayInputStream; 44 import java.io.OutputStream; 45 import java.util.Iterator; 46 import java.util.List; 47 import java.util.logging.Level; 48 import java.util.logging.Logger; 49 50 import javax.activation.*; 51 import javax.xml.soap.*; 52 import com.sun.xml.internal.org.jvnet.mimepull.MIMEPart; 53 54 /** 55 * Implementation of attachments. 56 * 57 * @author Anil Vijendran (akv@eng.sun.com) 58 */ 59 public class AttachmentPartImpl extends AttachmentPart { 60 61 protected static final Logger log = 62 Logger.getLogger(LogDomainConstants.SOAP_DOMAIN, 63 "com.sun.xml.internal.messaging.saaj.soap.LocalStrings"); 64 65 private final MimeHeaders headers; 66 private MimeBodyPart rawContent = null; 67 private DataHandler dataHandler = null; 68 69 //alternate impl that uses a MIMEPart 70 private MIMEPart mimePart = null; 71 72 public AttachmentPartImpl() { 73 headers = new MimeHeaders(); 74 75 // initialization from here should cover most of cases; 76 // if not, it would be necessary to call 77 // AttachmentPartImpl.initializeJavaActivationHandlers() 78 // explicitly by programmer 79 initializeJavaActivationHandlers(); 80 } 81 82 public AttachmentPartImpl(MIMEPart part) { 83 headers = new MimeHeaders(); 84 mimePart = part; 85 List<? extends com.sun.xml.internal.org.jvnet.mimepull.Header> hdrs = part.getAllHeaders(); 86 for (com.sun.xml.internal.org.jvnet.mimepull.Header hd : hdrs) { 87 headers.addHeader(hd.getName(), hd.getValue()); 88 } 89 } 90 91 @Override 92 public int getSize() throws SOAPException { 93 if (mimePart != null) { 94 try { 95 return mimePart.read().available(); 96 } catch (IOException e) { 97 return -1; 98 } 99 } 100 if ((rawContent == null) && (dataHandler == null)) 101 return 0; 102 103 if (rawContent != null) { 104 try { 105 return rawContent.getSize(); 106 } catch (Exception ex) { 107 log.log( 108 Level.SEVERE, 109 "SAAJ0573.soap.attachment.getrawbytes.ioexception", 110 new String[] { ex.getLocalizedMessage()}); 111 throw new SOAPExceptionImpl("Raw InputStream Error: " + ex); 112 } 113 } else { 114 ByteOutputStream bout = new ByteOutputStream(); 115 try { 116 dataHandler.writeTo(bout); 117 } catch (IOException ex) { 118 log.log( 119 Level.SEVERE, 120 "SAAJ0501.soap.data.handler.err", 121 new String[] { ex.getLocalizedMessage()}); 122 throw new SOAPExceptionImpl("Data handler error: " + ex); 123 } 124 return bout.size(); 125 } 126 } 127 128 @Override 129 public void clearContent() { 130 if (mimePart != null) { 131 mimePart.close(); 132 mimePart = null; 133 } 134 dataHandler = null; 135 rawContent = null; 136 } 137 138 @Override 139 public Object getContent() throws SOAPException { 140 try { 141 if (mimePart != null) { 142 //return an inputstream 143 return mimePart.read(); 144 } 145 if (dataHandler != null) { 146 return getDataHandler().getContent(); 147 } else if (rawContent != null) { 148 return rawContent.getContent(); 149 } else { 150 log.severe("SAAJ0572.soap.no.content.for.attachment"); 151 throw new SOAPExceptionImpl("No data handler/content associated with this attachment"); 152 } 153 } catch (Exception ex) { 154 log.log(Level.SEVERE, "SAAJ0575.soap.attachment.getcontent.exception", ex); 155 throw new SOAPExceptionImpl(ex.getLocalizedMessage()); 156 } 157 } 158 159 @Override 160 public void setContent(Object object, String contentType) 161 throws IllegalArgumentException { 162 if (mimePart != null) { 163 mimePart.close(); 164 mimePart = null; 165 } 166 DataHandler dh = new DataHandler(object, contentType); 167 168 setDataHandler(dh); 169 } 170 171 172 @Override 173 public DataHandler getDataHandler() throws SOAPException { 174 if (mimePart != null) { 175 //return an inputstream 176 return new DataHandler(new DataSource() { 177 178 @Override 179 public InputStream getInputStream() throws IOException { 180 return mimePart.read(); 181 } 182 183 @Override 184 public OutputStream getOutputStream() throws IOException { 185 throw new UnsupportedOperationException("getOutputStream cannot be supported : You have enabled LazyAttachments Option"); 186 } 187 188 @Override 189 public String getContentType() { 190 return mimePart.getContentType(); 191 } 192 193 @Override 194 public String getName() { 195 return "MIMEPart Wrapper DataSource"; 196 } 197 }); 198 } 199 if (dataHandler == null) { 200 if (rawContent != null) { 201 return new DataHandler(new MimePartDataSource(rawContent)); 202 } 203 log.severe("SAAJ0502.soap.no.handler.for.attachment"); 204 throw new SOAPExceptionImpl("No data handler associated with this attachment"); 205 } 206 return dataHandler; 207 } 208 209 @Override 210 public void setDataHandler(DataHandler dataHandler) 211 throws IllegalArgumentException { 212 if (mimePart != null) { 213 mimePart.close(); 214 mimePart = null; 215 } 216 if (dataHandler == null) { 217 log.severe("SAAJ0503.soap.no.null.to.dataHandler"); 218 throw new IllegalArgumentException("Null dataHandler argument to setDataHandler"); 219 } 220 this.dataHandler = dataHandler; 221 rawContent = null; 222 223 if (log.isLoggable(Level.FINE)) 224 log.log(Level.FINE, "SAAJ0580.soap.set.Content-Type", 225 new String[] { dataHandler.getContentType() }); 226 setMimeHeader("Content-Type", dataHandler.getContentType()); 227 } 228 229 @Override 230 public void removeAllMimeHeaders() { 231 headers.removeAllHeaders(); 232 } 233 234 @Override 235 public void removeMimeHeader(String header) { 236 headers.removeHeader(header); 237 } 238 239 @Override 240 public String[] getMimeHeader(String name) { 241 return headers.getHeader(name); 242 } 243 244 @Override 245 public void setMimeHeader(String name, String value) { 246 headers.setHeader(name, value); 247 } 248 249 @Override 250 public void addMimeHeader(String name, String value) { 251 headers.addHeader(name, value); 252 } 253 254 @Override 255 public Iterator<MimeHeader> getAllMimeHeaders() { 256 return headers.getAllHeaders(); 257 } 258 259 @Override 260 public Iterator<MimeHeader> getMatchingMimeHeaders(String[] names) { 261 return headers.getMatchingHeaders(names); 262 } 263 264 @Override 265 public Iterator<MimeHeader> getNonMatchingMimeHeaders(String[] names) { 266 return headers.getNonMatchingHeaders(names); 267 } 268 269 boolean hasAllHeaders(MimeHeaders hdrs) { 270 if (hdrs != null) { 271 Iterator i = hdrs.getAllHeaders(); 272 while (i.hasNext()) { 273 MimeHeader hdr = (MimeHeader) i.next(); 274 String[] values = headers.getHeader(hdr.getName()); 275 boolean found = false; 276 277 if (values != null) { 278 for (int j = 0; j < values.length; j++) 279 if (hdr.getValue().equalsIgnoreCase(values[j])) { 280 found = true; 281 break; 282 } 283 } 284 285 if (!found) { 286 return false; 287 } 288 } 289 } 290 return true; 291 } 292 293 MimeBodyPart getMimePart() throws SOAPException { 294 try { 295 if (this.mimePart != null) { 296 return new MimeBodyPart(mimePart); 297 } 298 if (rawContent != null) { 299 copyMimeHeaders(headers, rawContent); 300 return rawContent; 301 } 302 303 MimeBodyPart envelope = new MimeBodyPart(); 304 305 envelope.setDataHandler(dataHandler); 306 copyMimeHeaders(headers, envelope); 307 308 return envelope; 309 } catch (Exception ex) { 310 log.severe("SAAJ0504.soap.cannot.externalize.attachment"); 311 throw new SOAPExceptionImpl("Unable to externalize attachment", ex); 312 } 313 } 314 315 public static void copyMimeHeaders(MimeHeaders headers, MimeBodyPart mbp) 316 throws SOAPException { 317 318 Iterator i = headers.getAllHeaders(); 319 320 while (i.hasNext()) 321 try { 322 MimeHeader mh = (MimeHeader) i.next(); 323 324 mbp.setHeader(mh.getName(), mh.getValue()); 325 } catch (Exception ex) { 326 log.severe("SAAJ0505.soap.cannot.copy.mime.hdr"); 327 throw new SOAPExceptionImpl("Unable to copy MIME header", ex); 328 } 329 } 330 331 public static void copyMimeHeaders(MimeBodyPart mbp, AttachmentPartImpl ap) 332 throws SOAPException { 333 try { 334 List hdr = mbp.getAllHeaders(); 335 int sz = hdr.size(); 336 for( int i=0; i<sz; i++ ) { 337 Header h = (Header)hdr.get(i); 338 if(h.getName().equalsIgnoreCase("Content-Type")) 339 continue; // skip 340 ap.addMimeHeader(h.getName(), h.getValue()); 341 } 342 } catch (Exception ex) { 343 log.severe("SAAJ0506.soap.cannot.copy.mime.hdrs.into.attachment"); 344 throw new SOAPExceptionImpl( 345 "Unable to copy MIME headers into attachment", 346 ex); 347 } 348 } 349 350 @Override 351 public void setBase64Content(InputStream content, String contentType) 352 throws SOAPException { 353 354 if (mimePart != null) { 355 mimePart.close(); 356 mimePart = null; 357 } 358 dataHandler = null; 359 InputStream decoded = null; 360 ByteOutputStream bos = null; 361 try { 362 decoded = MimeUtility.decode(content, "base64"); 363 InternetHeaders hdrs = new InternetHeaders(); 364 hdrs.setHeader("Content-Type", contentType); 365 //TODO: reading the entire attachment here is ineffcient. Somehow the MimeBodyPart 366 // Ctor with inputStream causes problems based on the InputStream 367 // has markSupported()==true 368 bos = new ByteOutputStream(); 369 bos.write(decoded); 370 rawContent = new MimeBodyPart(hdrs, bos.getBytes(), bos.getCount()); 371 setMimeHeader("Content-Type", contentType); 372 } catch (Exception e) { 373 log.log(Level.SEVERE, "SAAJ0578.soap.attachment.setbase64content.exception", e); 374 throw new SOAPExceptionImpl(e.getLocalizedMessage()); 375 } finally { 376 if (bos != null) 377 bos.close(); 378 try { 379 if (decoded != null) 380 decoded.close(); 381 } catch (IOException ex) { 382 throw new SOAPException(ex); 383 } 384 } 385 } 386 387 @Override 388 public InputStream getBase64Content() throws SOAPException { 389 InputStream stream; 390 if (mimePart != null) { 391 stream = mimePart.read(); 392 } else if (rawContent != null) { 393 try { 394 stream = rawContent.getInputStream(); 395 } catch (Exception e) { 396 log.log(Level.SEVERE,"SAAJ0579.soap.attachment.getbase64content.exception", e); 397 throw new SOAPExceptionImpl(e.getLocalizedMessage()); 398 } 399 } else if (dataHandler != null) { 400 try { 401 stream = dataHandler.getInputStream(); 402 } catch (IOException e) { 403 log.severe("SAAJ0574.soap.attachment.datahandler.ioexception"); 404 throw new SOAPExceptionImpl("DataHandler error" + e); 405 } 406 } else { 407 log.severe("SAAJ0572.soap.no.content.for.attachment"); 408 throw new SOAPExceptionImpl("No data handler/content associated with this attachment"); 409 } 410 411 //TODO: Write a BASE64EncoderInputStream instead, 412 // this code below is inefficient 413 // where we are trying to read the whole attachment first 414 int len; 415 int size = 1024; 416 byte [] buf; 417 if (stream != null) { 418 try { 419 ByteArrayOutputStream bos = new ByteArrayOutputStream(size); 420 //TODO: try and optimize this on the same lines as 421 // ByteOutputStream : to eliminate the temp buffer here 422 OutputStream ret = MimeUtility.encode(bos, "base64"); 423 buf = new byte[size]; 424 while ((len = stream.read(buf, 0, size)) != -1) { 425 ret.write(buf, 0, len); 426 } 427 ret.flush(); 428 buf = bos.toByteArray(); 429 return new ByteArrayInputStream(buf); 430 } catch (Exception e) { 431 // throw new SOAPException 432 log.log(Level.SEVERE,"SAAJ0579.soap.attachment.getbase64content.exception", e); 433 throw new SOAPExceptionImpl(e.getLocalizedMessage()); 434 } finally { 435 try { 436 stream.close(); 437 } catch (IOException ex) { 438 //close the stream 439 } 440 } 441 } else { 442 //throw new SOAPException 443 log.log(Level.SEVERE,"SAAJ0572.soap.no.content.for.attachment"); 444 throw new SOAPExceptionImpl("No data handler/content associated with this attachment"); 445 } 446 } 447 448 @Override 449 public void setRawContent(InputStream content, String contentType) 450 throws SOAPException { 451 if (mimePart != null) { 452 mimePart.close(); 453 mimePart = null; 454 } 455 dataHandler = null; 456 ByteOutputStream bos = null; 457 try { 458 InternetHeaders hdrs = new InternetHeaders(); 459 hdrs.setHeader("Content-Type", contentType); 460 //TODO: reading the entire attachment here is ineffcient. Somehow the MimeBodyPart 461 // Ctor with inputStream causes problems based on whether the InputStream has 462 // markSupported()==true or false 463 bos = new ByteOutputStream(); 464 bos.write(content); 465 rawContent = new MimeBodyPart(hdrs, bos.getBytes(), bos.getCount()); 466 setMimeHeader("Content-Type", contentType); 467 } catch (Exception e) { 468 log.log(Level.SEVERE, "SAAJ0576.soap.attachment.setrawcontent.exception", e); 469 throw new SOAPExceptionImpl(e.getLocalizedMessage()); 470 } finally { 471 if (bos != null) 472 bos.close(); 473 try { 474 content.close(); 475 } catch (IOException ex) { 476 throw new SOAPException(ex); 477 } 478 } 479 } 480 481 /* 482 public void setRawContentBytes(byte[] content, String contentType) 483 throws SOAPException { 484 if (content == null) { 485 throw new SOAPExceptionImpl("Null content passed to setRawContentBytes"); 486 } 487 dataHandler = null; 488 try { 489 InternetHeaders hdrs = new InternetHeaders(); 490 hdrs.setHeader("Content-Type", contentType); 491 rawContent = new MimeBodyPart(hdrs, content, content.length); 492 setMimeHeader("Content-Type", contentType); 493 } catch (Exception e) { 494 log.log(Level.SEVERE, "SAAJ0576.soap.attachment.setrawcontent.exception", e); 495 throw new SOAPExceptionImpl(e.getLocalizedMessage()); 496 } 497 } */ 498 499 @Override 500 public void setRawContentBytes( 501 byte[] content, int off, int len, String contentType) 502 throws SOAPException { 503 if (mimePart != null) { 504 mimePart.close(); 505 mimePart = null; 506 } 507 if (content == null) { 508 throw new SOAPExceptionImpl("Null content passed to setRawContentBytes"); 509 } 510 dataHandler = null; 511 try { 512 InternetHeaders hdrs = new InternetHeaders(); 513 hdrs.setHeader("Content-Type", contentType); 514 rawContent = new MimeBodyPart(hdrs, content, off, len); 515 setMimeHeader("Content-Type", contentType); 516 } catch (Exception e) { 517 log.log(Level.SEVERE, 518 "SAAJ0576.soap.attachment.setrawcontent.exception", e); 519 throw new SOAPExceptionImpl(e.getLocalizedMessage()); 520 } 521 } 522 523 @Override 524 public InputStream getRawContent() throws SOAPException { 525 if (mimePart != null) { 526 return mimePart.read(); 527 } 528 if (rawContent != null) { 529 try { 530 return rawContent.getInputStream(); 531 } catch (Exception e) { 532 log.log(Level.SEVERE,"SAAJ0577.soap.attachment.getrawcontent.exception", e); 533 throw new SOAPExceptionImpl(e.getLocalizedMessage()); 534 } 535 } else if (dataHandler != null) { 536 try { 537 return dataHandler.getInputStream(); 538 } catch (IOException e) { 539 log.severe("SAAJ0574.soap.attachment.datahandler.ioexception"); 540 throw new SOAPExceptionImpl("DataHandler error" + e); 541 } 542 } else { 543 log.severe("SAAJ0572.soap.no.content.for.attachment"); 544 throw new SOAPExceptionImpl("No data handler/content associated with this attachment"); 545 } 546 } 547 548 @Override 549 public byte[] getRawContentBytes() throws SOAPException { 550 InputStream ret; 551 if (mimePart != null) { 552 try { 553 ret = mimePart.read(); 554 return ASCIIUtility.getBytes(ret); 555 } catch (IOException ex) { 556 log.log(Level.SEVERE,"SAAJ0577.soap.attachment.getrawcontent.exception", ex); 557 throw new SOAPExceptionImpl(ex); 558 } 559 } 560 if (rawContent != null) { 561 try { 562 ret = rawContent.getInputStream(); 563 return ASCIIUtility.getBytes(ret); 564 } catch (Exception e) { 565 log.log(Level.SEVERE,"SAAJ0577.soap.attachment.getrawcontent.exception", e); 566 throw new SOAPExceptionImpl(e); 567 } 568 } else if (dataHandler != null) { 569 try { 570 ret = dataHandler.getInputStream(); 571 return ASCIIUtility.getBytes(ret); 572 } catch (IOException e) { 573 log.severe("SAAJ0574.soap.attachment.datahandler.ioexception"); 574 throw new SOAPExceptionImpl("DataHandler error" + e); 575 } 576 } else { 577 log.severe("SAAJ0572.soap.no.content.for.attachment"); 578 throw new SOAPExceptionImpl("No data handler/content associated with this attachment"); 579 } 580 } 581 582 // attachments are equal if they are the same reference 583 @Override 584 public boolean equals(Object o) { 585 return (this == o); 586 } 587 588 // In JDK 8 we get a warning if we implement equals() but not hashCode(). 589 // There is no intuitive value for this, the default one in Object is fine. 590 @Override 591 public int hashCode() { 592 return super.hashCode(); 593 } 594 595 public MimeHeaders getMimeHeaders() { 596 return headers; 597 } 598 599 public static void initializeJavaActivationHandlers() { 600 // DataHandler.writeTo() may search for DCH. So adding some default ones. 601 try { 602 CommandMap map = CommandMap.getDefaultCommandMap(); 603 if (map instanceof MailcapCommandMap) { 604 MailcapCommandMap mailMap = (MailcapCommandMap) map; 605 606 // registering our DCH since javamail's DCH doesn't handle 607 if (!cmdMapInitialized(mailMap)) { 608 mailMap.addMailcap("text/xml;;x-java-content-handler=com.sun.xml.internal.messaging.saaj.soap.XmlDataContentHandler"); 609 mailMap.addMailcap("application/xml;;x-java-content-handler=com.sun.xml.internal.messaging.saaj.soap.XmlDataContentHandler"); 610 mailMap.addMailcap("application/fastinfoset;;x-java-content-handler=com.sun.xml.internal.messaging.saaj.soap.FastInfosetDataContentHandler"); 611 //mailMap.addMailcap("multipart/*;;x-java-content-handler=com.sun.xml.internal.messaging.saaj.soap.MultipartDataContentHandler"); 612 mailMap.addMailcap("image/*;;x-java-content-handler=com.sun.xml.internal.messaging.saaj.soap.ImageDataContentHandler"); 613 mailMap.addMailcap("text/plain;;x-java-content-handler=com.sun.xml.internal.messaging.saaj.soap.StringDataContentHandler"); 614 } 615 } 616 } catch (Throwable t) { 617 // ignore the exception. 618 } 619 } 620 621 private static boolean cmdMapInitialized(MailcapCommandMap mailMap) { 622 623 // checking fastinfoset handler, since this one is specific to SAAJ 624 CommandInfo[] commands = mailMap.getAllCommands("application/fastinfoset"); 625 if (commands == null || commands.length == 0) { 626 return false; 627 } 628 629 String saajClassName = "com.sun.xml.internal.ws.binding.FastInfosetDataContentHandler"; 630 for (CommandInfo command : commands) { 631 String commandClass = command.getCommandClass(); 632 if (saajClassName.equals(commandClass)) { 633 return true; 634 } 635 } 636 return false; 637 } 638 }