1 /*
   2  * Copyright (c) 1997, 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 com.sun.xml.internal.messaging.saaj.soap;
  27 
  28 import java.io.*;
  29 import java.util.*;
  30 import java.util.logging.Level;
  31 import java.util.logging.Logger;
  32 
  33 import javax.activation.DataHandler;
  34 import javax.activation.DataSource;
  35 import javax.xml.soap.*;
  36 import javax.xml.stream.XMLStreamReader;
  37 import javax.xml.transform.Source;
  38 import javax.xml.transform.stax.StAXSource;
  39 import javax.xml.transform.stream.StreamSource;
  40 
  41 import com.sun.xml.internal.messaging.saaj.packaging.mime.Header;
  42 import com.sun.xml.internal.messaging.saaj.packaging.mime.internet.*;
  43 import com.sun.xml.internal.messaging.saaj.packaging.mime.util.*;
  44 import com.sun.xml.internal.messaging.saaj.packaging.mime.MessagingException;
  45 
  46 import com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl;
  47 import com.sun.xml.internal.messaging.saaj.soap.impl.EnvelopeImpl;
  48 import com.sun.xml.internal.messaging.saaj.util.*;
  49 import com.sun.xml.internal.org.jvnet.mimepull.MIMEPart;
  50 
  51 /**
  52  * The message implementation for SOAP messages with
  53  * attachments. Messages for specific profiles will likely extend this
  54  * MessageImpl class and add more value for that particular profile.
  55  *
  56  * @author Anil Vijendran (akv@eng.sun.com)
  57  * @author Rajiv Mordani (rajiv.mordani@sun.com)
  58  * @author Manveen Kaur (manveen.kaur@sun.com)
  59  */
  60 
  61 public abstract class MessageImpl
  62     extends SOAPMessage
  63     implements SOAPConstants {
  64 
  65 
  66     public static final String CONTENT_ID             = "Content-ID";
  67     public static final String CONTENT_LOCATION       = "Content-Location";
  68 
  69     protected static final Logger log =
  70         Logger.getLogger(LogDomainConstants.SOAP_DOMAIN,
  71                          "com.sun.xml.internal.messaging.saaj.soap.LocalStrings");
  72 
  73     protected static final int PLAIN_XML_FLAG      = 1;      // 00001
  74     protected static final int MIME_MULTIPART_FLAG = 2;      // 00010
  75     protected static final int SOAP1_1_FLAG = 4;             // 00100
  76     protected static final int SOAP1_2_FLAG = 8;             // 01000
  77     //protected static final int MIME_MULTIPART_XOP_FLAG = 14; // 01110
  78     protected static final int MIME_MULTIPART_XOP_SOAP1_1_FLAG = 6;  // 00110
  79     protected static final int MIME_MULTIPART_XOP_SOAP1_2_FLAG = 10; // 01010
  80     protected static final int XOP_FLAG = 13;                // 01101
  81     protected static final int FI_ENCODED_FLAG     = 16;     // 10000
  82 
  83     protected MimeHeaders headers;
  84     protected ContentType contentType;
  85     protected SOAPPartImpl soapPartImpl;
  86     protected FinalArrayList attachments;
  87     protected boolean saved = false;
  88     protected byte[] messageBytes;
  89     protected int messageByteCount;
  90     protected HashMap properties = new HashMap();
  91 
  92     // used for lazy attachment initialization
  93     protected MimeMultipart multiPart = null;
  94     protected boolean attachmentsInitialized = false;
  95 
  96     /**
  97      * True if this part is encoded using Fast Infoset.
  98      * MIME -> application/fastinfoset
  99      */
 100     protected boolean isFastInfoset = false;
 101 
 102     /**
 103      * True if the Accept header of this message includes
 104      * application/fastinfoset
 105      */
 106     protected boolean acceptFastInfoset = false;
 107 
 108     protected MimeMultipart mmp = null;
 109 
 110     // if attachments are present, don't read the entire message in byte stream in saveTo()
 111     private boolean optimizeAttachmentProcessing = true;
 112 
 113     private InputStream inputStreamAfterSaveChanges = null;
 114 
 115     public static final String LAZY_SOAP_BODY_PARSING = "saaj.lazy.soap.body";
 116 
 117     // switch back to old MimeMultipart incase of problem
 118     private static boolean switchOffBM = false;
 119     private static boolean switchOffLazyAttachment = false;
 120     private static boolean useMimePull = false;
 121 
 122     static {
 123             String s = SAAJUtil.getSystemProperty("saaj.mime.optimization");
 124             if ((s != null) && s.equals("false")) {
 125                 switchOffBM = true;
 126             }
 127             s = SAAJUtil.getSystemProperty("saaj.lazy.mime.optimization");
 128             if ((s != null) && s.equals("false")) {
 129                 switchOffLazyAttachment = true;
 130             }
 131             useMimePull = SAAJUtil.getSystemBoolean("saaj.use.mimepull");
 132 
 133     }
 134 
 135     //property to indicate optimized serialization for lazy attachments
 136     private boolean lazyAttachments = false;
 137 
 138     // most of the times, Content-Types are already all lower cased.
 139     // String.toLowerCase() works faster in this case, so even if you
 140     // are only doing one comparison, it pays off to use String.toLowerCase()
 141     // than String.equalsIgnoreCase(). When you do more than one comparison,
 142     // the benefits of String.toLowerCase() dominates.
 143     //
 144     //
 145     // for FI,
 146     //   use application/fastinfoset for SOAP 1.1
 147     //   use application/soap+fastinfoset for SOAP 1.2
 148     // to speed up comparisons, test methods always use lower cases.
 149 
 150     /**
 151      * @param primary
 152      *      must be all lower case
 153      * @param sub
 154      *      must be all lower case
 155      */
 156     private static boolean isSoap1_1Type(String primary, String sub) {
 157         return primary.equalsIgnoreCase("text") && sub.equalsIgnoreCase("xml")
 158             || primary.equalsIgnoreCase("text") && sub.equalsIgnoreCase("xml-soap")
 159             || primary.equals("application")
 160                && sub.equals("fastinfoset");
 161     }
 162 
 163     /**
 164      * @param type
 165      *      must be all lower case
 166      */
 167     private static boolean isEqualToSoap1_1Type(String type) {
 168         return type.startsWith("text/xml") ||
 169                type.startsWith("application/fastinfoset");
 170     }
 171 
 172     /**
 173      * @param primary
 174      *      must be all lower case
 175      * @param sub
 176      *      must be all lower case
 177      */
 178     private static boolean isSoap1_2Type(String primary, String sub) {
 179         return primary.equals("application")
 180                && (sub.equals("soap+xml")
 181                    || sub.equals("soap+fastinfoset"));
 182     }
 183 
 184     /**
 185      * @param type
 186      *      must be all lower case
 187      */
 188     private static boolean isEqualToSoap1_2Type(String type) {
 189         return type.startsWith("application/soap+xml") ||
 190                type.startsWith("application/soap+fastinfoset");
 191     }
 192 
 193     /**
 194       * Construct a new message. This will be invoked before message
 195       * sends.
 196       */
 197     protected MessageImpl() {
 198         this(false, false);
 199         attachmentsInitialized = true;
 200     }
 201 
 202     /**
 203       * Construct a new message. This will be invoked before message
 204       * sends.
 205       */
 206     protected MessageImpl(boolean isFastInfoset, boolean acceptFastInfoset) {
 207         this.isFastInfoset = isFastInfoset;
 208         this.acceptFastInfoset = acceptFastInfoset;
 209 
 210         headers = new MimeHeaders();
 211         headers.setHeader("Accept", getExpectedAcceptHeader());
 212         contentType = new ContentType();
 213     }
 214 
 215     /**
 216      * Shallow copy.
 217      */
 218     protected MessageImpl(SOAPMessage msg) {
 219         if (!(msg instanceof MessageImpl)) {
 220             // don't know how to handle this.
 221         }
 222         MessageImpl src = (MessageImpl) msg;
 223         this.headers = src.headers;
 224         this.soapPartImpl = src.soapPartImpl;
 225         this.attachments = src.attachments;
 226         this.saved = src.saved;
 227         this.messageBytes = src.messageBytes;
 228         this.messageByteCount = src.messageByteCount;
 229         this.properties = src.properties;
 230         this.contentType = src.contentType;
 231     }
 232 
 233     /**
 234      * @param stat
 235      *      the mask value obtained from {@link #identifyContentType(ContentType)}
 236      */
 237     protected static boolean isSoap1_1Content(int stat) {
 238         return (stat & SOAP1_1_FLAG) != 0;
 239     }
 240 
 241     /**
 242      * @param stat
 243      *      the mask value obtained from {@link #identifyContentType(ContentType)}
 244      */
 245     protected static boolean isSoap1_2Content(int stat) {
 246         return (stat & SOAP1_2_FLAG) != 0;
 247     }
 248 
 249      private static boolean isMimeMultipartXOPSoap1_2Package(ContentType contentType) {
 250         String type = contentType.getParameter("type");
 251         if (type == null) {
 252             return false;
 253         }
 254         type = type.toLowerCase();
 255         if (!type.startsWith("application/xop+xml")) {
 256             return false;
 257         }
 258         String startinfo = contentType.getParameter("start-info");
 259         if (startinfo == null) {
 260             return false;
 261         }
 262         startinfo = startinfo.toLowerCase();
 263         return isEqualToSoap1_2Type(startinfo);
 264     }
 265 
 266 
 267      //private static boolean isMimeMultipartXOPPackage(ContentType contentType) {
 268      private static boolean isMimeMultipartXOPSoap1_1Package(ContentType contentType) {
 269         String type = contentType.getParameter("type");
 270         if(type==null)
 271             return false;
 272 
 273         type = type.toLowerCase();
 274         if(!type.startsWith("application/xop+xml"))
 275             return false;
 276 
 277         String startinfo = contentType.getParameter("start-info");
 278         if(startinfo == null)
 279             return false;
 280         startinfo = startinfo.toLowerCase();
 281         return isEqualToSoap1_1Type(startinfo);
 282     }
 283 
 284     private static boolean isSOAPBodyXOPPackage(ContentType contentType){
 285         String primary = contentType.getPrimaryType();
 286         String sub = contentType.getSubType();
 287 
 288         if (primary.equalsIgnoreCase("application")) {
 289             if (sub.equalsIgnoreCase("xop+xml")) {
 290                 String type = getTypeParameter(contentType);
 291                 return isEqualToSoap1_2Type(type) || isEqualToSoap1_1Type(type);
 292             }
 293         }
 294         return false;
 295     }
 296 
 297     /**
 298      * Construct a message from an input stream. When messages are
 299      * received, there's two parts -- the transport headers and the
 300      * message content in a transport specific stream.
 301      */
 302     protected MessageImpl(MimeHeaders headers, final InputStream in)
 303         throws SOAPExceptionImpl {
 304         contentType = parseContentType(headers);
 305         init(headers,identifyContentType(contentType),contentType,in);
 306     }
 307 
 308     private static ContentType parseContentType(MimeHeaders headers) throws SOAPExceptionImpl {
 309         final String ct;
 310         if (headers != null)
 311             ct = getContentType(headers);
 312         else {
 313             log.severe("SAAJ0550.soap.null.headers");
 314             throw new SOAPExceptionImpl("Cannot create message: " +
 315                                         "Headers can't be null");
 316         }
 317 
 318         if (ct == null) {
 319             log.severe("SAAJ0532.soap.no.Content-Type");
 320             throw new SOAPExceptionImpl("Absent Content-Type");
 321         }
 322         try {
 323             return new ContentType(ct);
 324         } catch (Throwable ex) {
 325             log.severe("SAAJ0535.soap.cannot.internalize.message");
 326             throw new SOAPExceptionImpl("Unable to internalize message", ex);
 327         }
 328     }
 329 
 330     /**
 331      * Construct a message from an input stream. When messages are
 332      * received, there's two parts -- the transport headers and the
 333      * message content in a transport specific stream.
 334      *
 335      * @param contentType
 336      *      The parsed content type header from the headers variable.
 337      *      This is redundant parameter, but it avoids reparsing this header again.
 338      * @param stat
 339      *      The result of {@link #identifyContentType(ContentType)} over
 340      *      the contentType parameter. This redundant parameter, but it avoids
 341      *      recomputing this information again.
 342      */
 343     protected MessageImpl(MimeHeaders headers, final ContentType contentType, int stat, final InputStream in) throws SOAPExceptionImpl {
 344         init(headers, stat, contentType, in);
 345 
 346     }
 347 
 348     public MessageImpl(MimeHeaders headers, ContentType ct, int stat,
 349             XMLStreamReader reader) throws SOAPExceptionImpl {
 350         init(headers, stat, ct, reader);
 351     }
 352 
 353     private void init(MimeHeaders headers, int stat, final ContentType contentType, final Object input) throws SOAPExceptionImpl {
 354         this.headers = headers;
 355 
 356         try {
 357 
 358             // Set isFastInfoset/acceptFastInfoset flag based on MIME type
 359             if ((stat & FI_ENCODED_FLAG) > 0) {
 360                 isFastInfoset = acceptFastInfoset = true;
 361             }
 362 
 363             // If necessary, inspect Accept header to set acceptFastInfoset
 364             if (!isFastInfoset) {
 365                 String[] values = headers.getHeader("Accept");
 366                 if (values != null) {
 367                     for (int i = 0; i < values.length; i++) {
 368                         StringTokenizer st = new StringTokenizer(values[i], ",");
 369                         while (st.hasMoreTokens()) {
 370                             final String token = st.nextToken().trim();
 371                             if (token.equalsIgnoreCase("application/fastinfoset") ||
 372                                 token.equalsIgnoreCase("application/soap+fastinfoset")) {
 373                                 acceptFastInfoset = true;
 374                                 break;
 375                             }
 376                         }
 377                     }
 378                 }
 379             }
 380 
 381             if (!isCorrectSoapVersion(stat)) {
 382                 log.log(
 383                     Level.SEVERE,
 384                     "SAAJ0533.soap.incorrect.Content-Type",
 385                     new String[] {
 386                         contentType.toString(),
 387                         getExpectedContentType()});
 388                 throw new SOAPVersionMismatchException(
 389                     "Cannot create message: incorrect content-type for SOAP version. Got: "
 390                         + contentType
 391                         + " Expected: "
 392                         + getExpectedContentType());
 393             }
 394             InputStream in = null;
 395             XMLStreamReader rdr = null;
 396             if (input instanceof InputStream) {
 397                in = (InputStream) input;
 398             } else {
 399               //is a StAX reader
 400                 rdr = (XMLStreamReader) input;
 401             }
 402             if ((stat & PLAIN_XML_FLAG) != 0) {
 403                 if (in != null) {
 404                     if (isFastInfoset) {
 405                         getSOAPPart().setContent(
 406                                 FastInfosetReflection.FastInfosetSource_new(in));
 407                     } else {
 408                         initCharsetProperty(contentType);
 409                         getSOAPPart().setContent(new StreamSource(in));
 410                     }
 411                 } else {
 412                     //is a StAX reader
 413                     if (isFastInfoset) {
 414                         //need to get FI stax reader
 415                     } else {
 416                         initCharsetProperty(contentType);
 417                         getSOAPPart().setContent(new StAXSource(rdr));
 418                     }
 419                 }
 420             }
 421             else if ((stat & MIME_MULTIPART_FLAG) != 0 && in == null) {
 422                 //only parse multipart in the inputstream case
 423                 //in stax reader case, we would be given the attachments separately
 424                 getSOAPPart().setContent(new StAXSource(rdr));
 425             } else if ((stat & MIME_MULTIPART_FLAG) != 0) {
 426                 final InputStream finalIn = in;
 427                 DataSource ds = new DataSource() {
 428                     public InputStream getInputStream() {
 429                         return finalIn;
 430                     }
 431 
 432                     public OutputStream getOutputStream() {
 433                         return null;
 434                     }
 435 
 436                     public String getContentType() {
 437                         return contentType.toString();
 438                     }
 439 
 440                     public String getName() {
 441                         return "";
 442                     }
 443                 };
 444 
 445                 multiPart = null;
 446                 if (useMimePull) {
 447                     multiPart = new MimePullMultipart(ds,contentType);
 448                 } else if (switchOffBM) {
 449                     multiPart = new MimeMultipart(ds,contentType);
 450                 } else {
 451                     multiPart = new BMMimeMultipart(ds,contentType);
 452                 }
 453 
 454                 String startParam = contentType.getParameter("start");
 455                 MimeBodyPart soapMessagePart = null;
 456                 InputStream soapPartInputStream = null;
 457                 String contentID = null;
 458                 String contentIDNoAngle = null;
 459                 if (switchOffBM || switchOffLazyAttachment) {
 460                     if(startParam == null) {
 461                         soapMessagePart = multiPart.getBodyPart(0);
 462                         for (int i = 1; i < multiPart.getCount(); i++) {
 463                             initializeAttachment(multiPart, i);
 464                         }
 465                     } else {
 466                         soapMessagePart = multiPart.getBodyPart(startParam);
 467                         for (int i = 0; i < multiPart.getCount(); i++) {
 468                             contentID = multiPart.getBodyPart(i).getContentID();
 469                             // Old versions of AXIS2 put angle brackets around the content
 470                             // id but not the start param
 471                             contentIDNoAngle = (contentID != null) ?
 472                                 contentID.replaceFirst("^<", "").replaceFirst(">$", "") : null;
 473                             if(!startParam.equals(contentID) && !startParam.equals(contentIDNoAngle))
 474                                 initializeAttachment(multiPart, i);
 475                         }
 476                     }
 477                 } else {
 478                     if (useMimePull) {
 479                         MimePullMultipart mpMultipart = (MimePullMultipart)multiPart;
 480                         MIMEPart sp = mpMultipart.readAndReturnSOAPPart();
 481                         soapMessagePart = new MimeBodyPart(sp);
 482                         soapPartInputStream = sp.readOnce();
 483                     } else {
 484                         BMMimeMultipart bmMultipart =
 485                                 (BMMimeMultipart) multiPart;
 486                         InputStream stream = bmMultipart.initStream();
 487 
 488                         SharedInputStream sin = null;
 489                         if (stream instanceof SharedInputStream) {
 490                             sin = (SharedInputStream) stream;
 491                         }
 492 
 493                         String boundary = "--" +
 494                                 contentType.getParameter("boundary");
 495                         byte[] bndbytes = ASCIIUtility.getBytes(boundary);
 496                         if (startParam == null) {
 497                             soapMessagePart =
 498                                     bmMultipart.getNextPart(stream, bndbytes, sin);
 499                             bmMultipart.removeBodyPart(soapMessagePart);
 500                         } else {
 501                             MimeBodyPart bp = null;
 502                             try {
 503                                while (!startParam.equals(contentID) && !startParam.equals(contentIDNoAngle)) {
 504                                     bp = bmMultipart.getNextPart(
 505                                             stream, bndbytes, sin);
 506                                     contentID = bp.getContentID();
 507                                     // Old versions of AXIS2 put angle brackets around the content
 508                                     // id but not the start param
 509                                     contentIDNoAngle = (contentID != null) ?
 510                                         contentID.replaceFirst("^<", "").replaceFirst(">$", "") : null;
 511                                 }
 512                                 soapMessagePart = bp;
 513                                 bmMultipart.removeBodyPart(bp);
 514                             } catch (Exception e) {
 515                                 throw new SOAPExceptionImpl(e);
 516                             }
 517                         }
 518                     }
 519                 }
 520 
 521                 // findbugs correctly points out that we'd NPE instantiating
 522                 // the ContentType (just below here) if soapMessagePart were
 523                 // null.  Hence are better off throwing a controlled exception
 524                 // at this point if it is null.
 525                 if (soapMessagePart == null) {
 526                     log.severe("SAAJ0510.soap.cannot.create.envelope");
 527                     throw new SOAPExceptionImpl(
 528                         "Unable to create envelope from given source: SOAP part not found");
 529                 }
 530 
 531                 if (soapPartInputStream == null) {
 532                     soapPartInputStream = soapMessagePart.getInputStream();
 533                 }
 534 
 535                 ContentType soapPartCType = new ContentType(
 536                                             soapMessagePart.getContentType());
 537                 initCharsetProperty(soapPartCType);
 538                 String baseType = soapPartCType.getBaseType().toLowerCase();
 539                 if(!(isEqualToSoap1_1Type(baseType)
 540                   || isEqualToSoap1_2Type(baseType)
 541                   || isSOAPBodyXOPPackage(soapPartCType))) {
 542                     log.log(Level.SEVERE,
 543                             "SAAJ0549.soap.part.invalid.Content-Type",
 544                             new Object[] {baseType});
 545                     throw new SOAPExceptionImpl(
 546                             "Bad Content-Type for SOAP Part : " +
 547                             baseType);
 548                 }
 549 
 550                 SOAPPart soapPart = getSOAPPart();
 551                 setMimeHeaders(soapPart, soapMessagePart);
 552                 soapPart.setContent(isFastInfoset ?
 553                      (Source) FastInfosetReflection.FastInfosetSource_new(
 554                          soapPartInputStream) :
 555                      (Source) new StreamSource(soapPartInputStream));
 556             } else {
 557                 log.severe("SAAJ0534.soap.unknown.Content-Type");
 558                 throw new SOAPExceptionImpl("Unrecognized Content-Type");
 559             }
 560         } catch (Throwable ex) {
 561             log.severe("SAAJ0535.soap.cannot.internalize.message");
 562             throw new SOAPExceptionImpl("Unable to internalize message", ex);
 563         }
 564         needsSave();
 565     }
 566 
 567     public boolean isFastInfoset() {
 568         return isFastInfoset;
 569     }
 570 
 571     public boolean acceptFastInfoset() {
 572         return acceptFastInfoset;
 573     }
 574 
 575     public void setIsFastInfoset(boolean value) {
 576         if (value != isFastInfoset) {
 577             isFastInfoset = value;
 578             if (isFastInfoset) {
 579                 acceptFastInfoset = true;
 580             }
 581             saved = false;      // ensure transcoding if necessary
 582         }
 583     }
 584 
 585     public boolean isLazySoapBodyParsing() {
 586         Object lazyParsingProp = getProperty(LAZY_SOAP_BODY_PARSING);
 587         if (lazyParsingProp == null) return false;
 588         if (lazyParsingProp instanceof Boolean) {
 589             return ((Boolean) lazyParsingProp).booleanValue();
 590         } else {
 591             return Boolean.valueOf(lazyParsingProp.toString());
 592         }
 593     }
 594     public Object getProperty(String property) {
 595         return (String) properties.get(property);
 596     }
 597 
 598     public void setProperty(String property, Object value) {
 599         verify(property, value);
 600         properties.put(property, value);
 601     }
 602 
 603     private void verify(String property, Object value) {
 604         if (property.equalsIgnoreCase(SOAPMessage.WRITE_XML_DECLARATION)) {
 605             if (!("true".equals(value) || "false".equals(value)))
 606                 throw new RuntimeException(
 607                     property + " must have value false or true");
 608 
 609             try {
 610                 EnvelopeImpl env = (EnvelopeImpl) getSOAPPart().getEnvelope();
 611                 if ("true".equalsIgnoreCase((String)value)) {
 612                     env.setOmitXmlDecl("no");
 613                 } else if ("false".equalsIgnoreCase((String)value)) {
 614                     env.setOmitXmlDecl("yes");
 615                 }
 616             } catch (Exception e) {
 617                 log.log(Level.SEVERE, "SAAJ0591.soap.exception.in.set.property",
 618                     new Object[] {e.getMessage(), "javax.xml.soap.write-xml-declaration"});
 619                 throw new RuntimeException(e);
 620             }
 621             return;
 622         }
 623 
 624         if (property.equalsIgnoreCase(SOAPMessage.CHARACTER_SET_ENCODING)) {
 625             try {
 626                 ((EnvelopeImpl) getSOAPPart().getEnvelope()).setCharsetEncoding((String)value);
 627             } catch (Exception e) {
 628                 log.log(Level.SEVERE, "SAAJ0591.soap.exception.in.set.property",
 629                     new Object[] {e.getMessage(), "javax.xml.soap.character-set-encoding"});
 630                 throw new RuntimeException(e);
 631             }
 632         }
 633     }
 634 
 635     protected abstract boolean isCorrectSoapVersion(int contentTypeId);
 636 
 637     protected abstract String getExpectedContentType();
 638     protected abstract String getExpectedAcceptHeader();
 639 
 640     /**
 641      * Sniffs the Content-Type header so that we can determine how to process.
 642      *
 643      * <p>
 644      * In the absence of type attribute we assume it to be text/xml.
 645      * That would mean we're easy on accepting the message and
 646      * generate the correct thing (as the SWA spec also specifies
 647      * that the type parameter should always be text/xml)
 648      *
 649      * @return
 650      *      combination of flags, such as PLAIN_XML_CODE and MIME_MULTIPART_CODE.
 651      */
 652     // SOAP1.2 allow SOAP1.2 content type
 653     static int identifyContentType(ContentType ct)
 654         throws SOAPExceptionImpl {
 655         // TBD
 656         //    Is there anything else we need to verify here?
 657 
 658         String primary = ct.getPrimaryType().toLowerCase();
 659         String sub = ct.getSubType().toLowerCase();
 660 
 661         if (primary.equals("multipart")) {
 662             if (sub.equals("related")) {
 663                 String type = getTypeParameter(ct);
 664                 if (isEqualToSoap1_1Type(type)) {
 665                     return (type.equals("application/fastinfoset") ?
 666                            FI_ENCODED_FLAG : 0) | MIME_MULTIPART_FLAG | SOAP1_1_FLAG;
 667                 }
 668                 else if (isEqualToSoap1_2Type(type)) {
 669                     return (type.equals("application/soap+fastinfoset") ?
 670                            FI_ENCODED_FLAG : 0) | MIME_MULTIPART_FLAG | SOAP1_2_FLAG;
 671                 /*} else if (isMimeMultipartXOPPackage(ct)) {
 672                     return MIME_MULTIPART_XOP_FLAG;*/
 673                 } else if (isMimeMultipartXOPSoap1_1Package(ct)) {
 674                     return MIME_MULTIPART_XOP_SOAP1_1_FLAG;
 675                 } else if (isMimeMultipartXOPSoap1_2Package(ct)) {
 676                     return MIME_MULTIPART_XOP_SOAP1_2_FLAG;
 677                 } else {
 678                     log.severe("SAAJ0536.soap.content-type.mustbe.multipart");
 679                     throw new SOAPExceptionImpl(
 680                         "Content-Type needs to be Multipart/Related "
 681                             + "and with \"type=text/xml\" "
 682                             + "or \"type=application/soap+xml\"");
 683                 }
 684             } else {
 685                 log.severe("SAAJ0537.soap.invalid.content-type");
 686                 throw new SOAPExceptionImpl(
 687                     "Invalid Content-Type: " + primary + '/' + sub);
 688             }
 689         }
 690         else if (isSoap1_1Type(primary, sub)) {
 691             return (primary.equalsIgnoreCase("application")
 692                     && sub.equalsIgnoreCase("fastinfoset") ?
 693                         FI_ENCODED_FLAG : 0)
 694                    | PLAIN_XML_FLAG | SOAP1_1_FLAG;
 695         }
 696         else if (isSoap1_2Type(primary, sub)) {
 697             return (primary.equalsIgnoreCase("application")
 698                     && sub.equalsIgnoreCase("soap+fastinfoset") ?
 699                         FI_ENCODED_FLAG : 0)
 700                    | PLAIN_XML_FLAG | SOAP1_2_FLAG;
 701         } else if(isSOAPBodyXOPPackage(ct)){
 702             return XOP_FLAG;
 703         } else {
 704             log.severe("SAAJ0537.soap.invalid.content-type");
 705             throw new SOAPExceptionImpl(
 706                 "Invalid Content-Type:"
 707                     + primary
 708                     + '/'
 709                     + sub
 710                     + ". Is this an error message instead of a SOAP response?");
 711         }
 712     }
 713 
 714     /**
 715      * Obtains the type parameter of the Content-Type header. Defaults to "text/xml".
 716      */
 717     private static String getTypeParameter(ContentType contentType) {
 718         String p = contentType.getParameter("type");
 719         if(p!=null)
 720             return p.toLowerCase();
 721         else
 722             return "text/xml";
 723     }
 724 
 725     public MimeHeaders getMimeHeaders() {
 726         return this.headers;
 727     }
 728 
 729     final static String getContentType(MimeHeaders headers) {
 730         String[] values = headers.getHeader("Content-Type");
 731         if (values == null)
 732             return null;
 733         else
 734             return values[0];
 735     }
 736 
 737     /*
 738      * Get the complete ContentType value along with optional parameters.
 739      */
 740     public String getContentType() {
 741         return getContentType(this.headers);
 742     }
 743 
 744     public void setContentType(String type) {
 745         headers.setHeader("Content-Type", type);
 746         needsSave();
 747     }
 748 
 749     private ContentType contentType() {
 750         ContentType ct = null;
 751         try {
 752             String currentContent = getContentType();
 753             if (currentContent == null) {
 754                 return this.contentType;
 755             }
 756             ct = new ContentType(currentContent);
 757         } catch (Exception e) {
 758             // what to do here?
 759         }
 760         return ct;
 761     }
 762 
 763     /*
 764      * Return the MIME type string, without the parameters.
 765      */
 766     public String getBaseType() {
 767         return contentType().getBaseType();
 768     }
 769 
 770     public void setBaseType(String type) {
 771         ContentType ct = contentType();
 772         ct.setParameter("type", type);
 773         headers.setHeader("Content-Type", ct.toString());
 774         needsSave();
 775     }
 776 
 777     public String getAction() {
 778         return contentType().getParameter("action");
 779     }
 780 
 781     public void setAction(String action) {
 782         ContentType ct = contentType();
 783         ct.setParameter("action", action);
 784         headers.setHeader("Content-Type", ct.toString());
 785         needsSave();
 786     }
 787 
 788     public String getCharset() {
 789         return contentType().getParameter("charset");
 790     }
 791 
 792     public void setCharset(String charset) {
 793         ContentType ct = contentType();
 794         ct.setParameter("charset", charset);
 795         headers.setHeader("Content-Type", ct.toString());
 796         needsSave();
 797     }
 798 
 799     /**
 800      * All write methods (i.e setters) should call this method in
 801      * order to make sure that a save is necessary since the state
 802      * has been modified.
 803      */
 804     private final void needsSave() {
 805         saved = false;
 806     }
 807 
 808     public  boolean saveRequired() {
 809         return saved != true;
 810     }
 811 
 812     public String getContentDescription() {
 813         String[] values = headers.getHeader("Content-Description");
 814         if (values != null && values.length > 0)
 815             return values[0];
 816         return null;
 817     }
 818 
 819     public void setContentDescription(String description) {
 820         headers.setHeader("Content-Description", description);
 821         needsSave();
 822     }
 823 
 824     public abstract SOAPPart getSOAPPart();
 825 
 826     public void removeAllAttachments() {
 827         try {
 828             initializeAllAttachments();
 829         } catch (Exception e) {
 830             throw new RuntimeException(e);
 831         }
 832 
 833         if (attachments != null) {
 834             attachments.clear();
 835             needsSave();
 836         }
 837     }
 838 
 839     public int countAttachments() {
 840         try {
 841             initializeAllAttachments();
 842         } catch (Exception e) {
 843             throw new RuntimeException(e);
 844         }
 845         if (attachments != null)
 846             return attachments.size();
 847         return 0;
 848     }
 849 
 850     public void addAttachmentPart(AttachmentPart attachment) {
 851         try {
 852             initializeAllAttachments();
 853             this.optimizeAttachmentProcessing = true;
 854         } catch (Exception e) {
 855             throw new RuntimeException(e);
 856         }
 857         if (attachments == null)
 858             attachments = new FinalArrayList();
 859 
 860         attachments.add(attachment);
 861 
 862         needsSave();
 863     }
 864 
 865     static private final Iterator nullIter = Collections.EMPTY_LIST.iterator();
 866 
 867     public Iterator getAttachments() {
 868         try {
 869             initializeAllAttachments();
 870         } catch (Exception e) {
 871             throw new RuntimeException(e);
 872         }
 873         if (attachments == null)
 874             return nullIter;
 875         return attachments.iterator();
 876     }
 877 
 878     private void setFinalContentType(String charset) {
 879         ContentType ct = contentType();
 880         if (ct == null) {
 881             ct = new ContentType();
 882         }
 883         String[] split = getExpectedContentType().split("/");
 884         ct.setPrimaryType(split[0]);
 885         ct.setSubType(split[1]);
 886         ct.setParameter("charset", charset);
 887         headers.setHeader("Content-Type", ct.toString());
 888     }
 889 
 890     private class MimeMatchingIterator implements Iterator {
 891         public MimeMatchingIterator(MimeHeaders headers) {
 892             this.headers = headers;
 893             this.iter = attachments.iterator();
 894         }
 895 
 896         private Iterator iter;
 897         private MimeHeaders headers;
 898         private Object nextAttachment;
 899 
 900         public boolean hasNext() {
 901             if (nextAttachment == null)
 902                 nextAttachment = nextMatch();
 903             return nextAttachment != null;
 904         }
 905 
 906         public Object next() {
 907             if (nextAttachment != null) {
 908                 Object ret = nextAttachment;
 909                 nextAttachment = null;
 910                 return ret;
 911             }
 912 
 913             if (hasNext())
 914                 return nextAttachment;
 915 
 916             return null;
 917         }
 918 
 919         Object nextMatch() {
 920             while (iter.hasNext()) {
 921                 AttachmentPartImpl ap = (AttachmentPartImpl) iter.next();
 922                 if (ap.hasAllHeaders(headers))
 923                     return ap;
 924             }
 925             return null;
 926         }
 927 
 928         public void remove() {
 929             iter.remove();
 930         }
 931     }
 932 
 933     public Iterator getAttachments(MimeHeaders headers) {
 934         try {
 935             initializeAllAttachments();
 936         } catch (Exception e) {
 937             throw new RuntimeException(e);
 938         }
 939         if (attachments == null)
 940             return nullIter;
 941 
 942         return new MimeMatchingIterator(headers);
 943     }
 944 
 945     public void removeAttachments(MimeHeaders headers) {
 946         try {
 947             initializeAllAttachments();
 948         } catch (Exception e) {
 949             throw new RuntimeException(e);
 950         }
 951         if (attachments == null)
 952             return ;
 953 
 954         Iterator it =  new MimeMatchingIterator(headers);
 955         while (it.hasNext()) {
 956             int index = attachments.indexOf(it.next());
 957             attachments.set(index, null);
 958         }
 959         FinalArrayList f = new FinalArrayList();
 960         for (int i = 0; i < attachments.size(); i++) {
 961             if (attachments.get(i) != null) {
 962                 f.add(attachments.get(i));
 963             }
 964         }
 965         attachments = f;
 966        // needsSave();
 967     }
 968 
 969     public AttachmentPart createAttachmentPart() {
 970         return new AttachmentPartImpl();
 971     }
 972 
 973     public  AttachmentPart getAttachment(SOAPElement element)
 974         throws SOAPException {
 975         try {
 976             initializeAllAttachments();
 977         } catch (Exception e) {
 978             throw new RuntimeException(e);
 979         }
 980         String uri;
 981         String hrefAttr = element.getAttribute("href");
 982         if ("".equals(hrefAttr)) {
 983             Node node = getValueNodeStrict(element);
 984             String swaRef = null;
 985             if (node != null) {
 986                 swaRef = node.getValue();
 987             }
 988             if (swaRef == null || "".equals(swaRef)) {
 989                 return null;
 990             } else {
 991                 uri = swaRef;
 992             }
 993         } else {
 994             uri = hrefAttr;
 995         }
 996         return getAttachmentPart(uri);
 997     }
 998 
 999     private Node getValueNodeStrict(SOAPElement element) {
1000         Node node = (Node)element.getFirstChild();
1001         if (node != null) {
1002             if (node.getNextSibling() == null
1003                 && node.getNodeType() == org.w3c.dom.Node.TEXT_NODE) {
1004                 return node;
1005             } else {
1006                 return null;
1007             }
1008         }
1009         return null;
1010     }
1011 
1012 
1013     private AttachmentPart getAttachmentPart(String uri) throws SOAPException {
1014         AttachmentPart _part;
1015         try {
1016             if (uri.startsWith("cid:")) {
1017                 // rfc2392
1018                 uri = '<'+uri.substring("cid:".length())+'>';
1019 
1020                 MimeHeaders headersToMatch = new MimeHeaders();
1021                 headersToMatch.addHeader(CONTENT_ID, uri);
1022 
1023                 Iterator i = this.getAttachments(headersToMatch);
1024                 _part = (i == null) ? null : (AttachmentPart)i.next();
1025             } else {
1026                 // try content-location
1027                 MimeHeaders headersToMatch = new MimeHeaders();
1028                 headersToMatch.addHeader(CONTENT_LOCATION, uri);
1029 
1030                 Iterator i = this.getAttachments(headersToMatch);
1031                 _part = (i == null) ? null : (AttachmentPart)i.next();
1032             }
1033 
1034             // try  auto-generated JAXRPC CID
1035             if (_part == null) {
1036                 Iterator j = this.getAttachments();
1037 
1038                 while (j.hasNext()) {
1039                     AttachmentPart p = (AttachmentPart)j.next();
1040                     String cl = p.getContentId();
1041                     if (cl != null) {
1042                         // obtain the partname
1043                         int eqIndex = cl.indexOf("=");
1044                         if (eqIndex > -1) {
1045                             cl = cl.substring(1, eqIndex);
1046                             if (cl.equalsIgnoreCase(uri)) {
1047                                 _part = p;
1048                                  break;
1049                             }
1050                         }
1051                     }
1052                 }
1053             }
1054 
1055         } catch (Exception se) {
1056             log.log(Level.SEVERE, "SAAJ0590.soap.unable.to.locate.attachment", new Object[] {uri});
1057             throw new SOAPExceptionImpl(se);
1058         }
1059         return _part;
1060     }
1061 
1062     private final InputStream getHeaderBytes()
1063         throws IOException {
1064         SOAPPartImpl sp = (SOAPPartImpl) getSOAPPart();
1065         return sp.getContentAsStream();
1066     }
1067 
1068     private String convertToSingleLine(String contentType) {
1069         StringBuffer buffer = new StringBuffer();
1070         for (int i = 0; i < contentType.length(); i ++) {
1071             char c = contentType.charAt(i);
1072             if (c != '\r' && c != '\n' && c != '\t')
1073                 buffer.append(c);
1074         }
1075         return buffer.toString();
1076     }
1077 
1078     private MimeMultipart getMimeMessage() throws SOAPException {
1079         try {
1080             SOAPPartImpl soapPart = (SOAPPartImpl) getSOAPPart();
1081             MimeBodyPart mimeSoapPart = soapPart.getMimePart();
1082 
1083             /*
1084              * Get content type from this message instead of soapPart
1085              * to ensure agreement if soapPart is transcoded (XML <-> FI)
1086              */
1087             ContentType soapPartCtype = new ContentType(getExpectedContentType());
1088 
1089             if (!isFastInfoset) {
1090                 soapPartCtype.setParameter("charset", initCharset());
1091             }
1092             mimeSoapPart.setHeader("Content-Type", soapPartCtype.toString());
1093 
1094             MimeMultipart headerAndBody = null;
1095 
1096             if (!switchOffBM && !switchOffLazyAttachment &&
1097                    (multiPart != null) && !attachmentsInitialized) {
1098                 headerAndBody = new BMMimeMultipart();
1099                 headerAndBody.addBodyPart(mimeSoapPart);
1100                 if (attachments != null) {
1101                     for (Iterator eachAttachment = attachments.iterator();
1102                          eachAttachment.hasNext();) {
1103                         headerAndBody.addBodyPart(
1104                             ((AttachmentPartImpl) eachAttachment.next())
1105                                 .getMimePart());
1106                     }
1107                 }
1108                 InputStream in = ((BMMimeMultipart)multiPart).getInputStream();
1109                 if (!((BMMimeMultipart)multiPart).lastBodyPartFound() &&
1110                     !((BMMimeMultipart)multiPart).isEndOfStream()) {
1111                     ((BMMimeMultipart)headerAndBody).setInputStream(in);
1112                     ((BMMimeMultipart)headerAndBody).setBoundary(
1113                         ((BMMimeMultipart)multiPart).getBoundary());
1114                     ((BMMimeMultipart)headerAndBody).
1115                         setLazyAttachments(lazyAttachments);
1116                 }
1117 
1118             } else {
1119                 headerAndBody = new MimeMultipart();
1120                 headerAndBody.addBodyPart(mimeSoapPart);
1121 
1122                 for (Iterator eachAttachement = getAttachments();
1123                     eachAttachement.hasNext();
1124                     ) {
1125                     headerAndBody.addBodyPart(
1126                         ((AttachmentPartImpl) eachAttachement.next())
1127                             .getMimePart());
1128                 }
1129             }
1130 
1131             ContentType contentType = headerAndBody.getContentType();
1132 
1133             ParameterList l = contentType.getParameterList();
1134 
1135             // set content type depending on SOAP version
1136             l.set("type", getExpectedContentType());
1137             l.set("boundary", contentType.getParameter("boundary"));
1138             ContentType nct = new ContentType("multipart", "related", l);
1139 
1140             headers.setHeader(
1141                 "Content-Type",
1142                 convertToSingleLine(nct.toString()));
1143           // TBD
1144           //    Set content length MIME header here.
1145 
1146             return headerAndBody;
1147         } catch (SOAPException ex) {
1148             throw ex;
1149         } catch (Throwable ex) {
1150             log.severe("SAAJ0538.soap.cannot.convert.msg.to.multipart.obj");
1151             throw new SOAPExceptionImpl(
1152                 "Unable to convert SOAP message into "
1153                     + "a MimeMultipart object",
1154                 ex);
1155         }
1156     }
1157 
1158     private String initCharset() {
1159 
1160         String charset = null;
1161 
1162         String[] cts = getMimeHeaders().getHeader("Content-Type");
1163         if ((cts != null) && (cts[0] != null)) {
1164             charset = getCharsetString(cts[0]);
1165         }
1166 
1167         if (charset == null) {
1168             charset = (String) getProperty(CHARACTER_SET_ENCODING);
1169         }
1170 
1171         if (charset != null) {
1172             return charset;
1173         }
1174 
1175         return "utf-8";
1176     }
1177 
1178     private String getCharsetString(String s) {
1179         try {
1180             int index = s.indexOf(";");
1181             if(index < 0)
1182                 return null;
1183             ParameterList pl = new ParameterList(s.substring(index));
1184             return pl.get("charset");
1185         } catch(Exception e) {
1186             return null;
1187         }
1188     }
1189 
1190     public void saveChanges() throws SOAPException {
1191 
1192         // suck in all the data from the attachments and have it
1193         // ready for writing/sending etc.
1194 
1195         String charset = initCharset();
1196 
1197         /*if (countAttachments() == 0) {*/
1198         int attachmentCount = (attachments == null) ? 0 : attachments.size();
1199         if (attachmentCount == 0) {
1200             if (!switchOffBM && !switchOffLazyAttachment &&
1201                 !attachmentsInitialized && (multiPart != null)) {
1202                 // so there might be attachments
1203                 attachmentCount = 1;
1204             }
1205         }
1206 
1207         try {
1208             if ((attachmentCount == 0) && !hasXOPContent()) {
1209                 InputStream in;
1210                 try{
1211                 /*
1212                  * Not sure why this is called getHeaderBytes(), but it actually
1213                  * returns the whole message as a byte stream. This stream could
1214                  * be either XML of Fast depending on the mode.
1215                  */
1216                     in = getHeaderBytes();
1217                     // no attachments, hence this property can be false
1218                     this.optimizeAttachmentProcessing = false;
1219                     if (SOAPPartImpl.lazyContentLength) {
1220                         inputStreamAfterSaveChanges = in;
1221                     }
1222                 } catch (IOException ex) {
1223                     log.severe("SAAJ0539.soap.cannot.get.header.stream");
1224                     throw new SOAPExceptionImpl(
1225                             "Unable to get header stream in saveChanges: ",
1226                             ex);
1227                 }
1228 
1229                 if (in instanceof ByteInputStream) {
1230                     ByteInputStream bIn = (ByteInputStream)in;
1231                     messageBytes = bIn.getBytes();
1232                     messageByteCount = bIn.getCount();
1233                 }
1234 
1235                 setFinalContentType(charset);
1236                 /*
1237                 headers.setHeader(
1238                         "Content-Type",
1239                         getExpectedContentType() +
1240                         (isFastInfoset ? "" : "; charset=" + charset));*/
1241                 if (messageByteCount > 0) {
1242                     headers.setHeader(
1243                             "Content-Length",
1244                             Integer.toString(messageByteCount));
1245                 }
1246             } else {
1247                 if(hasXOPContent())
1248                     mmp = getXOPMessage();
1249                 else
1250                     mmp = getMimeMessage();
1251             }
1252         } catch (Throwable ex) {
1253             log.severe("SAAJ0540.soap.err.saving.multipart.msg");
1254             throw new SOAPExceptionImpl(
1255                     "Error during saving a multipart message",
1256                     ex);
1257         }
1258 
1259         // FIX ME -- SOAP Action replaced by Content-Type optional parameter action
1260         /*
1261         if(isCorrectSoapVersion(SOAP1_1_FLAG)) {
1262 
1263             String[] soapAction = headers.getHeader("SOAPAction");
1264 
1265             if (soapAction == null || soapAction.length == 0)
1266                 headers.setHeader("SOAPAction", "\"\"");
1267 
1268         }
1269         */
1270 
1271         saved = true;
1272     }
1273 
1274     private MimeMultipart getXOPMessage() throws SOAPException {
1275         try {
1276             MimeMultipart headerAndBody = new MimeMultipart();
1277             SOAPPartImpl soapPart =  (SOAPPartImpl)getSOAPPart();
1278             MimeBodyPart mimeSoapPart = soapPart.getMimePart();
1279             ContentType soapPartCtype =
1280                 new ContentType("application/xop+xml");
1281             soapPartCtype.setParameter("type", getExpectedContentType());
1282             String charset = initCharset();
1283             soapPartCtype.setParameter("charset", charset);
1284             mimeSoapPart.setHeader("Content-Type", soapPartCtype.toString());
1285             headerAndBody.addBodyPart(mimeSoapPart);
1286 
1287             for (Iterator eachAttachement = getAttachments();
1288                 eachAttachement.hasNext();
1289                 ) {
1290                 headerAndBody.addBodyPart(
1291                     ((AttachmentPartImpl) eachAttachement.next())
1292                         .getMimePart());
1293             }
1294 
1295             ContentType contentType = headerAndBody.getContentType();
1296 
1297             ParameterList l = contentType.getParameterList();
1298 
1299             //lets not write start-info for now till we get servlet fix done
1300             l.set("start-info", getExpectedContentType());//+";charset="+initCharset());
1301 
1302             // set content type depending on SOAP version
1303             l.set("type", "application/xop+xml");
1304 
1305             if (isCorrectSoapVersion(SOAP1_2_FLAG)) {
1306                  String action = getAction();
1307                  if(action != null)
1308                      l.set("action", action);
1309             }
1310 
1311             l.set("boundary", contentType.getParameter("boundary"));
1312             ContentType nct = new ContentType("Multipart", "Related", l);
1313             headers.setHeader(
1314                 "Content-Type",
1315                 convertToSingleLine(nct.toString()));
1316             // TBD
1317             //    Set content length MIME header here.
1318 
1319             return headerAndBody;
1320         } catch (SOAPException ex) {
1321             throw ex;
1322         } catch (Throwable ex) {
1323             log.severe("SAAJ0538.soap.cannot.convert.msg.to.multipart.obj");
1324             throw new SOAPExceptionImpl(
1325                 "Unable to convert SOAP message into "
1326                     + "a MimeMultipart object",
1327                 ex);
1328         }
1329 
1330     }
1331 
1332     private boolean hasXOPContent() throws ParseException {
1333         String type = getContentType();
1334         if(type == null)
1335             return false;
1336         ContentType ct = new ContentType(type);
1337         //return isMimeMultipartXOPPackage(ct) || isSOAPBodyXOPPackage(ct);
1338         return isMimeMultipartXOPSoap1_1Package(ct) ||
1339             isMimeMultipartXOPSoap1_2Package(ct) || isSOAPBodyXOPPackage(ct);
1340 
1341     }
1342 
1343     public void writeTo(OutputStream out) throws SOAPException, IOException {
1344         if (saveRequired()){
1345             this.optimizeAttachmentProcessing = true;
1346             saveChanges();
1347         }
1348 
1349         if(!optimizeAttachmentProcessing){
1350             if (SOAPPartImpl.lazyContentLength && messageByteCount <= 0) {
1351                 byte[] buf = new byte[1024];
1352 
1353                 int length = 0;
1354                 while( (length = inputStreamAfterSaveChanges.read(buf)) != -1) {
1355                     out.write(buf,0, length);
1356                     messageByteCount += length;
1357                 }
1358                 if (messageByteCount > 0) {
1359                     headers.setHeader(
1360                             "Content-Length",
1361                             Integer.toString(messageByteCount));
1362                 }
1363             } else {
1364                 out.write(messageBytes, 0, messageByteCount);
1365             }
1366         }
1367         else{
1368             try{
1369                 if(hasXOPContent()){
1370                     mmp.writeTo(out);
1371                 }else{
1372                     mmp.writeTo(out);
1373                     if (!switchOffBM && !switchOffLazyAttachment &&
1374                             (multiPart != null) && !attachmentsInitialized) {
1375                         ((BMMimeMultipart)multiPart).setInputStream(
1376                                 ((BMMimeMultipart)mmp).getInputStream());
1377                     }
1378                 }
1379             } catch(Exception ex){
1380                 log.severe("SAAJ0540.soap.err.saving.multipart.msg");
1381                 throw new SOAPExceptionImpl(
1382                         "Error during saving a multipart message",
1383                         ex);
1384             }
1385         }
1386 
1387         if(isCorrectSoapVersion(SOAP1_1_FLAG)) {
1388 
1389             String[] soapAction = headers.getHeader("SOAPAction");
1390 
1391             if (soapAction == null || soapAction.length == 0)
1392                 headers.setHeader("SOAPAction", "\"\"");
1393 
1394         }
1395 
1396         messageBytes = null;
1397         needsSave();
1398     }
1399 
1400     public SOAPBody getSOAPBody() throws SOAPException {
1401         SOAPBody body = getSOAPPart().getEnvelope().getBody();
1402         /*if (body == null) {
1403              throw new SOAPException("No SOAP Body was found in the SOAP Message");
1404         }*/
1405         return body;
1406     }
1407 
1408     public SOAPHeader getSOAPHeader() throws SOAPException {
1409         SOAPHeader hdr = getSOAPPart().getEnvelope().getHeader();
1410         /*if (hdr == null) {
1411             throw new SOAPException("No SOAP Header was found in the SOAP Message");
1412         }*/
1413         return hdr;
1414     }
1415 
1416     private void initializeAllAttachments ()
1417         throws MessagingException, SOAPException {
1418         if (switchOffBM || switchOffLazyAttachment) {
1419             return;
1420         }
1421 
1422         if (attachmentsInitialized || (multiPart == null)) {
1423             return;
1424         }
1425 
1426         if (attachments == null)
1427             attachments = new FinalArrayList();
1428 
1429         int count = multiPart.getCount();
1430         for (int i=0; i < count; i++ ) {
1431             initializeAttachment(multiPart.getBodyPart(i));
1432         }
1433         attachmentsInitialized = true;
1434         //multiPart = null;
1435         needsSave();
1436      }
1437 
1438     private void initializeAttachment(MimeBodyPart mbp) throws SOAPException {
1439         AttachmentPartImpl attachmentPart = new AttachmentPartImpl();
1440         DataHandler attachmentHandler = mbp.getDataHandler();
1441         attachmentPart.setDataHandler(attachmentHandler);
1442 
1443         AttachmentPartImpl.copyMimeHeaders(mbp, attachmentPart);
1444         attachments.add(attachmentPart);
1445     }
1446 
1447     private void initializeAttachment(MimeMultipart multiPart, int i)
1448         throws Exception {
1449         MimeBodyPart currentBodyPart = multiPart.getBodyPart(i);
1450         AttachmentPartImpl attachmentPart = new AttachmentPartImpl();
1451 
1452         DataHandler attachmentHandler = currentBodyPart.getDataHandler();
1453         attachmentPart.setDataHandler(attachmentHandler);
1454 
1455         AttachmentPartImpl.copyMimeHeaders(currentBodyPart, attachmentPart);
1456         addAttachmentPart(attachmentPart);
1457     }
1458 
1459     private void setMimeHeaders(SOAPPart soapPart,
1460             MimeBodyPart soapMessagePart) throws Exception {
1461 
1462         // first remove the existing content-type
1463         soapPart.removeAllMimeHeaders();
1464         // add everything present in soapMessagePart
1465         List headers = soapMessagePart.getAllHeaders();
1466         int sz = headers.size();
1467         for( int i=0; i<sz; i++ ) {
1468             Header h = (Header) headers.get(i);
1469             soapPart.addMimeHeader(h.getName(), h.getValue());
1470         }
1471     }
1472 
1473     private void initCharsetProperty(ContentType contentType) {
1474         String charset = contentType.getParameter("charset");
1475         if (charset != null) {
1476             ((SOAPPartImpl) getSOAPPart()).setSourceCharsetEncoding(charset);
1477             if(!charset.equalsIgnoreCase("utf-8"))
1478                 setProperty(CHARACTER_SET_ENCODING, charset);
1479         }
1480     }
1481 
1482     public void setLazyAttachments(boolean flag) {
1483         lazyAttachments = flag;
1484     }
1485 
1486 }