Tue, 06 Mar 2012 16:09:35 -0800
7150322: Stop using drop source bundles in jaxws
Reviewed-by: darcy, ohrstrom
ohair@286 | 1 | /* |
ohair@286 | 2 | * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. |
ohair@286 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
ohair@286 | 4 | * |
ohair@286 | 5 | * This code is free software; you can redistribute it and/or modify it |
ohair@286 | 6 | * under the terms of the GNU General Public License version 2 only, as |
ohair@286 | 7 | * published by the Free Software Foundation. Oracle designates this |
ohair@286 | 8 | * particular file as subject to the "Classpath" exception as provided |
ohair@286 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
ohair@286 | 10 | * |
ohair@286 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
ohair@286 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
ohair@286 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
ohair@286 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
ohair@286 | 15 | * accompanied this code). |
ohair@286 | 16 | * |
ohair@286 | 17 | * You should have received a copy of the GNU General Public License version |
ohair@286 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
ohair@286 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
ohair@286 | 20 | * |
ohair@286 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
ohair@286 | 22 | * or visit www.oracle.com if you need additional information or have any |
ohair@286 | 23 | * questions. |
ohair@286 | 24 | */ |
ohair@286 | 25 | |
ohair@286 | 26 | /* |
ohair@286 | 27 | * @(#)MimeMultipart.java 1.31 03/01/29 |
ohair@286 | 28 | */ |
ohair@286 | 29 | |
ohair@286 | 30 | |
ohair@286 | 31 | |
ohair@286 | 32 | package com.sun.xml.internal.messaging.saaj.packaging.mime.internet; |
ohair@286 | 33 | |
ohair@286 | 34 | import java.io.*; |
ohair@286 | 35 | |
ohair@286 | 36 | import javax.activation.DataSource; |
ohair@286 | 37 | |
ohair@286 | 38 | import com.sun.xml.internal.messaging.saaj.packaging.mime.*; |
ohair@286 | 39 | import com.sun.xml.internal.messaging.saaj.packaging.mime.util.*; |
ohair@286 | 40 | import com.sun.xml.internal.messaging.saaj.util.FinalArrayList; |
ohair@286 | 41 | import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream; |
ohair@286 | 42 | import com.sun.xml.internal.messaging.saaj.util.SAAJUtil; |
ohair@286 | 43 | |
ohair@286 | 44 | /** |
ohair@286 | 45 | * The MimeMultipart class is an implementation |
ohair@286 | 46 | * that uses MIME conventions for the multipart data. <p> |
ohair@286 | 47 | * |
ohair@286 | 48 | * A MimeMultipart is obtained from a MimeBodyPart whose primary type |
ohair@286 | 49 | * is "multipart" (by invoking the part's <code>getContent()</code> method) |
ohair@286 | 50 | * or it can be created by a client as part of creating a new MimeMessage. <p> |
ohair@286 | 51 | * |
ohair@286 | 52 | * The default multipart subtype is "mixed". The other multipart |
ohair@286 | 53 | * subtypes, such as "alternative", "related", and so on, can be |
ohair@286 | 54 | * implemented as subclasses of MimeMultipart with additional methods |
ohair@286 | 55 | * to implement the additional semantics of that type of multipart |
ohair@286 | 56 | * content. The intent is that service providers, mail JavaBean writers |
ohair@286 | 57 | * and mail clients will write many such subclasses and their Command |
ohair@286 | 58 | * Beans, and will install them into the JavaBeans Activation |
ohair@286 | 59 | * Framework, so that any JavaMail implementation and its clients can |
ohair@286 | 60 | * transparently find and use these classes. Thus, a MIME multipart |
ohair@286 | 61 | * handler is treated just like any other type handler, thereby |
ohair@286 | 62 | * decoupling the process of providing multipart handlers from the |
ohair@286 | 63 | * JavaMail API. Lacking these additional MimeMultipart subclasses, |
ohair@286 | 64 | * all subtypes of MIME multipart data appear as MimeMultipart objects. <p> |
ohair@286 | 65 | * |
ohair@286 | 66 | * An application can directly construct a MIME multipart object of any |
ohair@286 | 67 | * subtype by using the <code>MimeMultipart(String subtype)</code> |
ohair@286 | 68 | * constructor. For example, to create a "multipart/alternative" object, |
ohair@286 | 69 | * use <code>new MimeMultipart("alternative")</code>. |
ohair@286 | 70 | * |
ohair@286 | 71 | * @version 1.31, 03/01/29 |
ohair@286 | 72 | * @author John Mani |
ohair@286 | 73 | * @author Bill Shannon |
ohair@286 | 74 | * @author Max Spivak |
ohair@286 | 75 | */ |
ohair@286 | 76 | |
ohair@286 | 77 | //BM MimeMultipart can extend this |
ohair@286 | 78 | public class MimeMultipart { |
ohair@286 | 79 | |
ohair@286 | 80 | /** |
ohair@286 | 81 | * The DataSource supplying our InputStream. |
ohair@286 | 82 | */ |
ohair@286 | 83 | protected DataSource ds = null; |
ohair@286 | 84 | |
ohair@286 | 85 | /** |
ohair@286 | 86 | * Have we parsed the data from our InputStream yet? |
ohair@286 | 87 | * Defaults to true; set to false when our constructor is |
ohair@286 | 88 | * given a DataSource with an InputStream that we need to |
ohair@286 | 89 | * parse. |
ohair@286 | 90 | */ |
ohair@286 | 91 | protected boolean parsed = true; |
ohair@286 | 92 | |
ohair@286 | 93 | /** |
ohair@286 | 94 | * Vector of MimeBodyPart objects. |
ohair@286 | 95 | */ |
ohair@286 | 96 | protected FinalArrayList parts = new FinalArrayList(); // Holds BodyParts |
ohair@286 | 97 | |
ohair@286 | 98 | /** |
ohair@286 | 99 | * This field specifies the content-type of this multipart |
ohair@286 | 100 | * object. It defaults to "multipart/mixed". |
ohair@286 | 101 | */ |
ohair@286 | 102 | protected ContentType contentType; |
ohair@286 | 103 | |
ohair@286 | 104 | /** |
ohair@286 | 105 | * The <code>MimeBodyPart</code> containing this <code>MimeMultipart</code>, |
ohair@286 | 106 | * if known. |
ohair@286 | 107 | * @since JavaMail 1.1 |
ohair@286 | 108 | */ |
ohair@286 | 109 | protected MimeBodyPart parent; |
ohair@286 | 110 | |
ohair@286 | 111 | protected static boolean ignoreMissingEndBoundary = true; |
ohair@286 | 112 | |
ohair@286 | 113 | static { |
ohair@286 | 114 | ignoreMissingEndBoundary = SAAJUtil.getSystemBoolean("saaj.mime.multipart.ignoremissingendboundary"); |
ohair@286 | 115 | } |
ohair@286 | 116 | /** |
ohair@286 | 117 | * Default constructor. An empty MimeMultipart object |
ohair@286 | 118 | * is created. Its content type is set to "multipart/mixed". |
ohair@286 | 119 | * A unique boundary string is generated and this string is |
ohair@286 | 120 | * setup as the "boundary" parameter for the |
ohair@286 | 121 | * <code>contentType</code> field. <p> |
ohair@286 | 122 | * |
ohair@286 | 123 | * MimeBodyParts may be added later. |
ohair@286 | 124 | */ |
ohair@286 | 125 | public MimeMultipart() { |
ohair@286 | 126 | this("mixed"); |
ohair@286 | 127 | } |
ohair@286 | 128 | |
ohair@286 | 129 | /** |
ohair@286 | 130 | * Construct a MimeMultipart object of the given subtype. |
ohair@286 | 131 | * A unique boundary string is generated and this string is |
ohair@286 | 132 | * setup as the "boundary" parameter for the |
ohair@286 | 133 | * <code>contentType</code> field. <p> |
ohair@286 | 134 | * |
ohair@286 | 135 | * MimeBodyParts may be added later. |
ohair@286 | 136 | */ |
ohair@286 | 137 | public MimeMultipart(String subtype) { |
ohair@286 | 138 | //super(); |
ohair@286 | 139 | /* |
ohair@286 | 140 | * Compute a boundary string. |
ohair@286 | 141 | */ |
ohair@286 | 142 | String boundary = UniqueValue.getUniqueBoundaryValue(); |
ohair@286 | 143 | contentType = new ContentType("multipart", subtype, null); |
ohair@286 | 144 | contentType.setParameter("boundary", boundary); |
ohair@286 | 145 | } |
ohair@286 | 146 | |
ohair@286 | 147 | /** |
ohair@286 | 148 | * Constructs a MimeMultipart object and its bodyparts from the |
ohair@286 | 149 | * given DataSource. <p> |
ohair@286 | 150 | * |
ohair@286 | 151 | * This constructor handles as a special case the situation where the |
ohair@286 | 152 | * given DataSource is a MultipartDataSource object. |
ohair@286 | 153 | * |
ohair@286 | 154 | * Otherwise, the DataSource is assumed to provide a MIME multipart |
ohair@286 | 155 | * byte stream. The <code>parsed</code> flag is set to false. When |
ohair@286 | 156 | * the data for the body parts are needed, the parser extracts the |
ohair@286 | 157 | * "boundary" parameter from the content type of this DataSource, |
ohair@286 | 158 | * skips the 'preamble' and reads bytes till the terminating |
ohair@286 | 159 | * boundary and creates MimeBodyParts for each part of the stream. |
ohair@286 | 160 | * |
ohair@286 | 161 | * @param ds DataSource, can be a MultipartDataSource |
ohair@286 | 162 | * @param ct |
ohair@286 | 163 | * This must be the same information as {@link DataSource#getContentType()}. |
ohair@286 | 164 | * All the callers of this method seem to have this object handy, so |
ohair@286 | 165 | * for performance reason this method accepts it. Can be null. |
ohair@286 | 166 | */ |
ohair@286 | 167 | public MimeMultipart(DataSource ds, ContentType ct) throws MessagingException { |
ohair@286 | 168 | // 'ds' was not a MultipartDataSource, we have |
ohair@286 | 169 | // to parse this ourself. |
ohair@286 | 170 | parsed = false; |
ohair@286 | 171 | this.ds = ds; |
ohair@286 | 172 | if (ct==null) |
ohair@286 | 173 | contentType = new ContentType(ds.getContentType()); |
ohair@286 | 174 | else |
ohair@286 | 175 | contentType = ct; |
ohair@286 | 176 | } |
ohair@286 | 177 | |
ohair@286 | 178 | /** |
ohair@286 | 179 | * Set the subtype. This method should be invoked only on a new |
ohair@286 | 180 | * MimeMultipart object created by the client. The default subtype |
ohair@286 | 181 | * of such a multipart object is "mixed". <p> |
ohair@286 | 182 | * |
ohair@286 | 183 | * @param subtype Subtype |
ohair@286 | 184 | */ |
ohair@286 | 185 | public void setSubType(String subtype) { |
ohair@286 | 186 | contentType.setSubType(subtype); |
ohair@286 | 187 | } |
ohair@286 | 188 | |
ohair@286 | 189 | /** |
ohair@286 | 190 | * Return the number of enclosed MimeBodyPart objects. |
ohair@286 | 191 | * |
ohair@286 | 192 | * @return number of parts |
ohair@286 | 193 | */ |
ohair@286 | 194 | public int getCount() throws MessagingException { |
ohair@286 | 195 | parse(); |
ohair@286 | 196 | if (parts == null) |
ohair@286 | 197 | return 0; |
ohair@286 | 198 | |
ohair@286 | 199 | return parts.size(); |
ohair@286 | 200 | } |
ohair@286 | 201 | |
ohair@286 | 202 | /** |
ohair@286 | 203 | * Get the specified MimeBodyPart. BodyParts are numbered starting at 0. |
ohair@286 | 204 | * |
ohair@286 | 205 | * @param index the index of the desired MimeBodyPart |
ohair@286 | 206 | * @return the MimeBodyPart |
ohair@286 | 207 | * @exception MessagingException if no such MimeBodyPart exists |
ohair@286 | 208 | */ |
ohair@286 | 209 | public MimeBodyPart getBodyPart(int index) |
ohair@286 | 210 | throws MessagingException { |
ohair@286 | 211 | parse(); |
ohair@286 | 212 | if (parts == null) |
ohair@286 | 213 | throw new IndexOutOfBoundsException("No such BodyPart"); |
ohair@286 | 214 | |
ohair@286 | 215 | return (MimeBodyPart)parts.get(index); |
ohair@286 | 216 | } |
ohair@286 | 217 | |
ohair@286 | 218 | /** |
ohair@286 | 219 | * Get the MimeBodyPart referred to by the given ContentID (CID). |
ohair@286 | 220 | * Returns null if the part is not found. |
ohair@286 | 221 | * |
ohair@286 | 222 | * @param CID the ContentID of the desired part |
ohair@286 | 223 | * @return the MimeBodyPart |
ohair@286 | 224 | */ |
ohair@286 | 225 | public MimeBodyPart getBodyPart(String CID) |
ohair@286 | 226 | throws MessagingException { |
ohair@286 | 227 | parse(); |
ohair@286 | 228 | |
ohair@286 | 229 | int count = getCount(); |
ohair@286 | 230 | for (int i = 0; i < count; i++) { |
ohair@286 | 231 | MimeBodyPart part = getBodyPart(i); |
ohair@286 | 232 | String s = part.getContentID(); |
ohair@286 | 233 | // Old versions of AXIS2 put angle brackets around the content |
ohair@286 | 234 | // id but not the start param |
ohair@286 | 235 | String sNoAngle = (s!= null) ? s.replaceFirst("^<", "").replaceFirst(">$", "") |
ohair@286 | 236 | :null; |
ohair@286 | 237 | if (s != null && (s.equals(CID) || CID.equals(sNoAngle))) |
ohair@286 | 238 | return part; |
ohair@286 | 239 | } |
ohair@286 | 240 | return null; |
ohair@286 | 241 | } |
ohair@286 | 242 | |
ohair@286 | 243 | /** |
ohair@286 | 244 | * Update headers. The default implementation here just |
ohair@286 | 245 | * calls the <code>updateHeaders</code> method on each of its |
ohair@286 | 246 | * children BodyParts. <p> |
ohair@286 | 247 | * |
ohair@286 | 248 | * Note that the boundary parameter is already set up when |
ohair@286 | 249 | * a new and empty MimeMultipart object is created. <p> |
ohair@286 | 250 | * |
ohair@286 | 251 | * This method is called when the <code>saveChanges</code> |
ohair@286 | 252 | * method is invoked on the Message object containing this |
ohair@286 | 253 | * MimeMultipart. This is typically done as part of the Message |
ohair@286 | 254 | * send process, however note that a client is free to call |
ohair@286 | 255 | * it any number of times. So if the header updating process is |
ohair@286 | 256 | * expensive for a specific MimeMultipart subclass, then it |
ohair@286 | 257 | * might itself want to track whether its internal state actually |
ohair@286 | 258 | * did change, and do the header updating only if necessary. |
ohair@286 | 259 | */ |
ohair@286 | 260 | protected void updateHeaders() throws MessagingException { |
ohair@286 | 261 | for (int i = 0; i < parts.size(); i++) |
ohair@286 | 262 | ((MimeBodyPart)parts.get(i)).updateHeaders(); |
ohair@286 | 263 | } |
ohair@286 | 264 | |
ohair@286 | 265 | /** |
ohair@286 | 266 | * Iterates through all the parts and outputs each Mime part |
ohair@286 | 267 | * separated by a boundary. |
ohair@286 | 268 | */ |
ohair@286 | 269 | public void writeTo(OutputStream os) |
ohair@286 | 270 | throws IOException, MessagingException { |
ohair@286 | 271 | parse(); |
ohair@286 | 272 | |
ohair@286 | 273 | String boundary = "--" + contentType.getParameter("boundary"); |
ohair@286 | 274 | |
ohair@286 | 275 | for (int i = 0; i < parts.size(); i++) { |
ohair@286 | 276 | OutputUtil.writeln(boundary, os); // put out boundary |
ohair@286 | 277 | getBodyPart(i).writeTo(os); |
ohair@286 | 278 | OutputUtil.writeln(os); // put out empty line |
ohair@286 | 279 | } |
ohair@286 | 280 | |
ohair@286 | 281 | // put out last boundary |
ohair@286 | 282 | OutputUtil.writeAsAscii(boundary, os); |
ohair@286 | 283 | OutputUtil.writeAsAscii("--", os); |
ohair@286 | 284 | os.flush(); |
ohair@286 | 285 | } |
ohair@286 | 286 | |
ohair@286 | 287 | /** |
ohair@286 | 288 | * Parse the InputStream from our DataSource, constructing the |
ohair@286 | 289 | * appropriate MimeBodyParts. The <code>parsed</code> flag is |
ohair@286 | 290 | * set to true, and if true on entry nothing is done. This |
ohair@286 | 291 | * method is called by all other methods that need data for |
ohair@286 | 292 | * the body parts, to make sure the data has been parsed. |
ohair@286 | 293 | * |
ohair@286 | 294 | * @since JavaMail 1.2 |
ohair@286 | 295 | */ |
ohair@286 | 296 | protected void parse() throws MessagingException { |
ohair@286 | 297 | if (parsed) |
ohair@286 | 298 | return; |
ohair@286 | 299 | |
ohair@286 | 300 | InputStream in; |
ohair@286 | 301 | SharedInputStream sin = null; |
ohair@286 | 302 | long start = 0, end = 0; |
ohair@286 | 303 | boolean foundClosingBoundary = false; |
ohair@286 | 304 | |
ohair@286 | 305 | try { |
ohair@286 | 306 | in = ds.getInputStream(); |
ohair@286 | 307 | if (!(in instanceof ByteArrayInputStream) && |
ohair@286 | 308 | !(in instanceof BufferedInputStream) && |
ohair@286 | 309 | !(in instanceof SharedInputStream)) |
ohair@286 | 310 | in = new BufferedInputStream(in); |
ohair@286 | 311 | } catch (Exception ex) { |
ohair@286 | 312 | throw new MessagingException("No inputstream from datasource"); |
ohair@286 | 313 | } |
ohair@286 | 314 | if (in instanceof SharedInputStream) |
ohair@286 | 315 | sin = (SharedInputStream)in; |
ohair@286 | 316 | |
ohair@286 | 317 | String boundary = "--" + contentType.getParameter("boundary"); |
ohair@286 | 318 | byte[] bndbytes = ASCIIUtility.getBytes(boundary); |
ohair@286 | 319 | int bl = bndbytes.length; |
ohair@286 | 320 | |
ohair@286 | 321 | try { |
ohair@286 | 322 | // Skip the preamble |
ohair@286 | 323 | LineInputStream lin = new LineInputStream(in); |
ohair@286 | 324 | String line; |
ohair@286 | 325 | while ((line = lin.readLine()) != null) { |
ohair@286 | 326 | /* |
ohair@286 | 327 | * Strip trailing whitespace. Can't use trim method |
ohair@286 | 328 | * because it's too aggressive. Some bogus MIME |
ohair@286 | 329 | * messages will include control characters in the |
ohair@286 | 330 | * boundary string. |
ohair@286 | 331 | */ |
ohair@286 | 332 | int i; |
ohair@286 | 333 | for (i = line.length() - 1; i >= 0; i--) { |
ohair@286 | 334 | char c = line.charAt(i); |
ohair@286 | 335 | if (!(c == ' ' || c == '\t')) |
ohair@286 | 336 | break; |
ohair@286 | 337 | } |
ohair@286 | 338 | line = line.substring(0, i + 1); |
ohair@286 | 339 | if (line.equals(boundary)) |
ohair@286 | 340 | break; |
ohair@286 | 341 | } |
ohair@286 | 342 | if (line == null) |
ohair@286 | 343 | throw new MessagingException("Missing start boundary"); |
ohair@286 | 344 | |
ohair@286 | 345 | /* |
ohair@286 | 346 | * Read and process body parts until we see the |
ohair@286 | 347 | * terminating boundary line (or EOF). |
ohair@286 | 348 | */ |
ohair@286 | 349 | boolean done = false; |
ohair@286 | 350 | getparts: |
ohair@286 | 351 | while (!done) { |
ohair@286 | 352 | InternetHeaders headers = null; |
ohair@286 | 353 | if (sin != null) { |
ohair@286 | 354 | start = sin.getPosition(); |
ohair@286 | 355 | // skip headers |
ohair@286 | 356 | while ((line = lin.readLine()) != null && line.length() > 0) |
ohair@286 | 357 | ; |
ohair@286 | 358 | if (line == null) { |
ohair@286 | 359 | if (!ignoreMissingEndBoundary) { |
ohair@286 | 360 | throw new MessagingException("Missing End Boundary for Mime Package : EOF while skipping headers"); |
ohair@286 | 361 | } |
ohair@286 | 362 | // assume there's just a missing end boundary |
ohair@286 | 363 | break getparts; |
ohair@286 | 364 | } |
ohair@286 | 365 | } else { |
ohair@286 | 366 | // collect the headers for this body part |
ohair@286 | 367 | headers = createInternetHeaders(in); |
ohair@286 | 368 | } |
ohair@286 | 369 | |
ohair@286 | 370 | if (!in.markSupported()) |
ohair@286 | 371 | throw new MessagingException("Stream doesn't support mark"); |
ohair@286 | 372 | |
ohair@286 | 373 | ByteOutputStream buf = null; |
ohair@286 | 374 | // if we don't have a shared input stream, we copy the data |
ohair@286 | 375 | if (sin == null) |
ohair@286 | 376 | buf = new ByteOutputStream(); |
ohair@286 | 377 | int b; |
ohair@286 | 378 | boolean bol = true; // beginning of line flag |
ohair@286 | 379 | // the two possible end of line characters |
ohair@286 | 380 | int eol1 = -1, eol2 = -1; |
ohair@286 | 381 | |
ohair@286 | 382 | /* |
ohair@286 | 383 | * Read and save the content bytes in buf. |
ohair@286 | 384 | */ |
ohair@286 | 385 | for (;;) { |
ohair@286 | 386 | if (bol) { |
ohair@286 | 387 | /* |
ohair@286 | 388 | * At the beginning of a line, check whether the |
ohair@286 | 389 | * next line is a boundary. |
ohair@286 | 390 | */ |
ohair@286 | 391 | int i; |
ohair@286 | 392 | in.mark(bl + 4 + 1000); // bnd + "--\r\n" + lots of LWSP |
ohair@286 | 393 | // read bytes, matching against the boundary |
ohair@286 | 394 | for (i = 0; i < bl; i++) |
ohair@286 | 395 | if (in.read() != bndbytes[i]) |
ohair@286 | 396 | break; |
ohair@286 | 397 | if (i == bl) { |
ohair@286 | 398 | // matched the boundary, check for last boundary |
ohair@286 | 399 | int b2 = in.read(); |
ohair@286 | 400 | if (b2 == '-') { |
ohair@286 | 401 | if (in.read() == '-') { |
ohair@286 | 402 | done = true; |
ohair@286 | 403 | foundClosingBoundary = true; |
ohair@286 | 404 | break; // ignore trailing text |
ohair@286 | 405 | } |
ohair@286 | 406 | } |
ohair@286 | 407 | // skip linear whitespace |
ohair@286 | 408 | while (b2 == ' ' || b2 == '\t') |
ohair@286 | 409 | b2 = in.read(); |
ohair@286 | 410 | // check for end of line |
ohair@286 | 411 | if (b2 == '\n') |
ohair@286 | 412 | break; // got it! break out of the loop |
ohair@286 | 413 | if (b2 == '\r') { |
ohair@286 | 414 | in.mark(1); |
ohair@286 | 415 | if (in.read() != '\n') |
ohair@286 | 416 | in.reset(); |
ohair@286 | 417 | break; // got it! break out of the loop |
ohair@286 | 418 | } |
ohair@286 | 419 | } |
ohair@286 | 420 | // failed to match, reset and proceed normally |
ohair@286 | 421 | in.reset(); |
ohair@286 | 422 | |
ohair@286 | 423 | // if this is not the first line, write out the |
ohair@286 | 424 | // end of line characters from the previous line |
ohair@286 | 425 | if (buf != null && eol1 != -1) { |
ohair@286 | 426 | buf.write(eol1); |
ohair@286 | 427 | if (eol2 != -1) |
ohair@286 | 428 | buf.write(eol2); |
ohair@286 | 429 | eol1 = eol2 = -1; |
ohair@286 | 430 | } |
ohair@286 | 431 | } |
ohair@286 | 432 | |
ohair@286 | 433 | // read the next byte |
ohair@286 | 434 | if ((b = in.read()) < 0) { |
ohair@286 | 435 | done = true; |
ohair@286 | 436 | break; |
ohair@286 | 437 | } |
ohair@286 | 438 | |
ohair@286 | 439 | /* |
ohair@286 | 440 | * If we're at the end of the line, save the eol characters |
ohair@286 | 441 | * to be written out before the beginning of the next line. |
ohair@286 | 442 | */ |
ohair@286 | 443 | if (b == '\r' || b == '\n') { |
ohair@286 | 444 | bol = true; |
ohair@286 | 445 | if (sin != null) |
ohair@286 | 446 | end = sin.getPosition() - 1; |
ohair@286 | 447 | eol1 = b; |
ohair@286 | 448 | if (b == '\r') { |
ohair@286 | 449 | in.mark(1); |
ohair@286 | 450 | if ((b = in.read()) == '\n') |
ohair@286 | 451 | eol2 = b; |
ohair@286 | 452 | else |
ohair@286 | 453 | in.reset(); |
ohair@286 | 454 | } |
ohair@286 | 455 | } else { |
ohair@286 | 456 | bol = false; |
ohair@286 | 457 | if (buf != null) |
ohair@286 | 458 | buf.write(b); |
ohair@286 | 459 | } |
ohair@286 | 460 | } |
ohair@286 | 461 | |
ohair@286 | 462 | /* |
ohair@286 | 463 | * Create a MimeBody element to represent this body part. |
ohair@286 | 464 | */ |
ohair@286 | 465 | MimeBodyPart part; |
ohair@286 | 466 | if (sin != null) |
ohair@286 | 467 | part = createMimeBodyPart(sin.newStream(start, end)); |
ohair@286 | 468 | else |
ohair@286 | 469 | part = createMimeBodyPart(headers, buf.getBytes(), buf.getCount()); |
ohair@286 | 470 | addBodyPart(part); |
ohair@286 | 471 | } |
ohair@286 | 472 | } catch (IOException ioex) { |
ohair@286 | 473 | throw new MessagingException("IO Error", ioex); |
ohair@286 | 474 | } |
ohair@286 | 475 | |
ohair@286 | 476 | if (!ignoreMissingEndBoundary && !foundClosingBoundary && sin== null) { |
ohair@286 | 477 | throw new MessagingException("Missing End Boundary for Mime Package : EOF while skipping headers"); |
ohair@286 | 478 | } |
ohair@286 | 479 | parsed = true; |
ohair@286 | 480 | } |
ohair@286 | 481 | |
ohair@286 | 482 | /** |
ohair@286 | 483 | * Create and return an InternetHeaders object that loads the |
ohair@286 | 484 | * headers from the given InputStream. Subclasses can override |
ohair@286 | 485 | * this method to return a subclass of InternetHeaders, if |
ohair@286 | 486 | * necessary. This implementation simply constructs and returns |
ohair@286 | 487 | * an InternetHeaders object. |
ohair@286 | 488 | * |
ohair@286 | 489 | * @param is the InputStream to read the headers from |
ohair@286 | 490 | * @exception MessagingException |
ohair@286 | 491 | * @since JavaMail 1.2 |
ohair@286 | 492 | */ |
ohair@286 | 493 | protected InternetHeaders createInternetHeaders(InputStream is) |
ohair@286 | 494 | throws MessagingException { |
ohair@286 | 495 | return new InternetHeaders(is); |
ohair@286 | 496 | } |
ohair@286 | 497 | |
ohair@286 | 498 | /** |
ohair@286 | 499 | * Create and return a MimeBodyPart object to represent a |
ohair@286 | 500 | * body part parsed from the InputStream. Subclasses can override |
ohair@286 | 501 | * this method to return a subclass of MimeBodyPart, if |
ohair@286 | 502 | * necessary. This implementation simply constructs and returns |
ohair@286 | 503 | * a MimeBodyPart object. |
ohair@286 | 504 | * |
ohair@286 | 505 | * @param headers the headers for the body part |
ohair@286 | 506 | * @param content the content of the body part |
ohair@286 | 507 | * @since JavaMail 1.2 |
ohair@286 | 508 | */ |
ohair@286 | 509 | protected MimeBodyPart createMimeBodyPart(InternetHeaders headers, byte[] content, int len) { |
ohair@286 | 510 | return new MimeBodyPart(headers, content,len); |
ohair@286 | 511 | } |
ohair@286 | 512 | |
ohair@286 | 513 | /** |
ohair@286 | 514 | * Create and return a MimeBodyPart object to represent a |
ohair@286 | 515 | * body part parsed from the InputStream. Subclasses can override |
ohair@286 | 516 | * this method to return a subclass of MimeBodyPart, if |
ohair@286 | 517 | * necessary. This implementation simply constructs and returns |
ohair@286 | 518 | * a MimeBodyPart object. |
ohair@286 | 519 | * |
ohair@286 | 520 | * @param is InputStream containing the body part |
ohair@286 | 521 | * @exception MessagingException |
ohair@286 | 522 | * @since JavaMail 1.2 |
ohair@286 | 523 | */ |
ohair@286 | 524 | protected MimeBodyPart createMimeBodyPart(InputStream is) throws MessagingException { |
ohair@286 | 525 | return new MimeBodyPart(is); |
ohair@286 | 526 | } |
ohair@286 | 527 | |
ohair@286 | 528 | /** |
ohair@286 | 529 | * Setup this MimeMultipart object from the given MultipartDataSource. <p> |
ohair@286 | 530 | * |
ohair@286 | 531 | * The method adds the MultipartDataSource's MimeBodyPart |
ohair@286 | 532 | * objects into this MimeMultipart. This MimeMultipart's contentType is |
ohair@286 | 533 | * set to that of the MultipartDataSource. <p> |
ohair@286 | 534 | * |
ohair@286 | 535 | * This method is typically used in those cases where one |
ohair@286 | 536 | * has a multipart data source that has already been pre-parsed into |
ohair@286 | 537 | * the individual body parts (for example, an IMAP datasource), but |
ohair@286 | 538 | * needs to create an appropriate MimeMultipart subclass that represents |
ohair@286 | 539 | * a specific multipart subtype. |
ohair@286 | 540 | * |
ohair@286 | 541 | * @param mp MimeMultipart datasource |
ohair@286 | 542 | */ |
ohair@286 | 543 | |
ohair@286 | 544 | protected void setMultipartDataSource(MultipartDataSource mp) |
ohair@286 | 545 | throws MessagingException { |
ohair@286 | 546 | contentType = new ContentType(mp.getContentType()); |
ohair@286 | 547 | |
ohair@286 | 548 | int count = mp.getCount(); |
ohair@286 | 549 | for (int i = 0; i < count; i++) |
ohair@286 | 550 | addBodyPart(mp.getBodyPart(i)); |
ohair@286 | 551 | } |
ohair@286 | 552 | |
ohair@286 | 553 | /** |
ohair@286 | 554 | * Return the content-type of this MimeMultipart. <p> |
ohair@286 | 555 | * |
ohair@286 | 556 | * This implementation just returns the value of the |
ohair@286 | 557 | * <code>contentType</code> field. |
ohair@286 | 558 | * |
ohair@286 | 559 | * @return content-type |
ohair@286 | 560 | * @see #contentType |
ohair@286 | 561 | */ |
ohair@286 | 562 | public ContentType getContentType() { |
ohair@286 | 563 | return contentType; |
ohair@286 | 564 | } |
ohair@286 | 565 | |
ohair@286 | 566 | /** |
ohair@286 | 567 | * Remove the specified part from the multipart message. |
ohair@286 | 568 | * Shifts all the parts after the removed part down one. |
ohair@286 | 569 | * |
ohair@286 | 570 | * @param part The part to remove |
ohair@286 | 571 | * @return true if part removed, false otherwise |
ohair@286 | 572 | * @exception MessagingException if no such MimeBodyPart exists |
ohair@286 | 573 | */ |
ohair@286 | 574 | public boolean removeBodyPart(MimeBodyPart part) throws MessagingException { |
ohair@286 | 575 | if (parts == null) |
ohair@286 | 576 | throw new MessagingException("No such body part"); |
ohair@286 | 577 | |
ohair@286 | 578 | boolean ret = parts.remove(part); |
ohair@286 | 579 | part.setParent(null); |
ohair@286 | 580 | return ret; |
ohair@286 | 581 | } |
ohair@286 | 582 | |
ohair@286 | 583 | /** |
ohair@286 | 584 | * Remove the part at specified location (starting from 0). |
ohair@286 | 585 | * Shifts all the parts after the removed part down one. |
ohair@286 | 586 | * |
ohair@286 | 587 | * @param index Index of the part to remove |
ohair@286 | 588 | * @exception IndexOutOfBoundsException if the given index |
ohair@286 | 589 | * is out of range. |
ohair@286 | 590 | */ |
ohair@286 | 591 | public void removeBodyPart(int index) { |
ohair@286 | 592 | if (parts == null) |
ohair@286 | 593 | throw new IndexOutOfBoundsException("No such BodyPart"); |
ohair@286 | 594 | |
ohair@286 | 595 | MimeBodyPart part = (MimeBodyPart)parts.get(index); |
ohair@286 | 596 | parts.remove(index); |
ohair@286 | 597 | part.setParent(null); |
ohair@286 | 598 | } |
ohair@286 | 599 | |
ohair@286 | 600 | /** |
ohair@286 | 601 | * Adds a MimeBodyPart to the multipart. The MimeBodyPart is appended to |
ohair@286 | 602 | * the list of existing Parts. |
ohair@286 | 603 | * |
ohair@286 | 604 | * @param part The MimeBodyPart to be appended |
ohair@286 | 605 | */ |
ohair@286 | 606 | public synchronized void addBodyPart(MimeBodyPart part) { |
ohair@286 | 607 | if (parts == null) |
ohair@286 | 608 | parts = new FinalArrayList(); |
ohair@286 | 609 | |
ohair@286 | 610 | parts.add(part); |
ohair@286 | 611 | part.setParent(this); |
ohair@286 | 612 | } |
ohair@286 | 613 | |
ohair@286 | 614 | /** |
ohair@286 | 615 | * Adds a MimeBodyPart at position <code>index</code>. |
ohair@286 | 616 | * If <code>index</code> is not the last one in the list, |
ohair@286 | 617 | * the subsequent parts are shifted up. If <code>index</code> |
ohair@286 | 618 | * is larger than the number of parts present, the |
ohair@286 | 619 | * MimeBodyPart is appended to the end. |
ohair@286 | 620 | * |
ohair@286 | 621 | * @param part The MimeBodyPart to be inserted |
ohair@286 | 622 | * @param index Location where to insert the part |
ohair@286 | 623 | */ |
ohair@286 | 624 | public synchronized void addBodyPart(MimeBodyPart part, int index) { |
ohair@286 | 625 | if (parts == null) |
ohair@286 | 626 | parts = new FinalArrayList(); |
ohair@286 | 627 | |
ohair@286 | 628 | parts.add(index,part); |
ohair@286 | 629 | part.setParent(this); |
ohair@286 | 630 | } |
ohair@286 | 631 | |
ohair@286 | 632 | /** |
ohair@286 | 633 | * Return the <code>MimeBodyPart</code> that contains this <code>MimeMultipart</code> |
ohair@286 | 634 | * object, or <code>null</code> if not known. |
ohair@286 | 635 | * @since JavaMail 1.1 |
ohair@286 | 636 | */ |
ohair@286 | 637 | MimeBodyPart getParent() { |
ohair@286 | 638 | return parent; |
ohair@286 | 639 | } |
ohair@286 | 640 | |
ohair@286 | 641 | /** |
ohair@286 | 642 | * Set the parent of this <code>MimeMultipart</code> to be the specified |
ohair@286 | 643 | * <code>MimeBodyPart</code>. Normally called by the <code>Message</code> |
ohair@286 | 644 | * or <code>MimeBodyPart</code> <code>setContent(MimeMultipart)</code> method. |
ohair@286 | 645 | * <code>parent</code> may be <code>null</code> if the |
ohair@286 | 646 | * <code>MimeMultipart</code> is being removed from its containing |
ohair@286 | 647 | * <code>MimeBodyPart</code>. |
ohair@286 | 648 | * @since JavaMail 1.1 |
ohair@286 | 649 | */ |
ohair@286 | 650 | void setParent(MimeBodyPart parent) { |
ohair@286 | 651 | this.parent = parent; |
ohair@286 | 652 | } |
ohair@286 | 653 | } |