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