1 /*
2 * Copyright (c) 1997, 2013, 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
116 /**
117 * Default constructor. An empty MimeMultipart object
118 * is created. Its content type is set to "multipart/mixed".
119 * A unique boundary string is generated and this string is
120 * setup as the "boundary" parameter for the
121 * <code>contentType</code> field. <p>
122 *
123 * MimeBodyParts may be added later.
124 */
125 public MimeMultipart() {
126 this("mixed");
127 }
128
129 /**
130 * Construct a MimeMultipart object of the given subtype.
131 * A unique boundary string is generated and this string is
132 * setup as the "boundary" parameter for the
133 * <code>contentType</code> field. <p>
134 *
135 * MimeBodyParts may be added later.
136 */
137 public MimeMultipart(String subtype) {
138 //super();
139 /*
140 * Compute a boundary string.
141 */
142 String boundary = UniqueValue.getUniqueBoundaryValue();
143 contentType = new ContentType("multipart", subtype, null);
144 contentType.setParameter("boundary", boundary);
145 }
146
147 /**
148 * Constructs a MimeMultipart object and its bodyparts from the
149 * given DataSource. <p>
150 *
151 * This constructor handles as a special case the situation where the
152 * given DataSource is a MultipartDataSource object.
153 *
154 * Otherwise, the DataSource is assumed to provide a MIME multipart
155 * byte stream. The <code>parsed</code> flag is set to false. When
156 * the data for the body parts are needed, the parser extracts the
157 * "boundary" parameter from the content type of this DataSource,
158 * skips the 'preamble' and reads bytes till the terminating
159 * boundary and creates MimeBodyParts for each part of the stream.
160 *
161 * @param ds DataSource, can be a MultipartDataSource
162 * @param ct
163 * This must be the same information as {@link DataSource#getContentType()}.
164 * All the callers of this method seem to have this object handy, so
165 * for performance reason this method accepts it. Can be null.
166 */
167 public MimeMultipart(DataSource ds, ContentType ct) throws MessagingException {
168 // 'ds' was not a MultipartDataSource, we have
169 // to parse this ourself.
170 parsed = false;
171 this.ds = ds;
172 if (ct==null)
173 contentType = new ContentType(ds.getContentType());
174 else
175 contentType = ct;
176 }
177
178 /**
179 * Set the subtype. This method should be invoked only on a new
180 * MimeMultipart object created by the client. The default subtype
181 * of such a multipart object is "mixed". <p>
182 *
183 * @param subtype Subtype
184 */
185 public void setSubType(String subtype) {
186 contentType.setSubType(subtype);
187 }
188
189 /**
190 * Return the number of enclosed MimeBodyPart objects.
191 *
192 * @return number of parts
193 */
194 public int getCount() throws MessagingException {
195 parse();
196 if (parts == null)
197 return 0;
198
199 return parts.size();
200 }
201
202 /**
203 * Get the specified MimeBodyPart. BodyParts are numbered starting at 0.
204 *
205 * @param index the index of the desired MimeBodyPart
206 * @return the MimeBodyPart
207 * @exception MessagingException if no such MimeBodyPart exists
208 */
209 public MimeBodyPart getBodyPart(int index)
210 throws MessagingException {
211 parse();
212 if (parts == null)
213 throw new IndexOutOfBoundsException("No such BodyPart");
214
215 return parts.get(index);
216 }
217
218 /**
219 * Get the MimeBodyPart referred to by the given ContentID (CID).
220 * Returns null if the part is not found.
221 *
222 * @param CID the ContentID of the desired part
223 * @return the MimeBodyPart
224 */
225 public MimeBodyPart getBodyPart(String CID)
226 throws MessagingException {
227 parse();
228
229 int count = getCount();
230 for (int i = 0; i < count; i++) {
231 MimeBodyPart part = getBodyPart(i);
232 String s = part.getContentID();
233 // Old versions of AXIS2 put angle brackets around the content
234 // id but not the start param
235 String sNoAngle = (s!= null) ? s.replaceFirst("^<", "").replaceFirst(">$", "")
236 :null;
237 if (s != null && (s.equals(CID) || CID.equals(sNoAngle)))
238 return part;
239 }
240 return null;
241 }
242
243 /**
244 * Update headers. The default implementation here just
245 * calls the <code>updateHeaders</code> method on each of its
246 * children BodyParts. <p>
247 *
248 * Note that the boundary parameter is already set up when
249 * a new and empty MimeMultipart object is created. <p>
250 *
251 * This method is called when the <code>saveChanges</code>
252 * method is invoked on the Message object containing this
253 * MimeMultipart. This is typically done as part of the Message
254 * send process, however note that a client is free to call
255 * it any number of times. So if the header updating process is
256 * expensive for a specific MimeMultipart subclass, then it
257 * might itself want to track whether its internal state actually
258 * did change, and do the header updating only if necessary.
259 */
260 protected void updateHeaders() throws MessagingException {
261 for (int i = 0; i < parts.size(); i++)
262 parts.get(i).updateHeaders();
263 }
264
265 /**
266 * Iterates through all the parts and outputs each Mime part
267 * separated by a boundary.
268 */
269 public void writeTo(OutputStream os)
270 throws IOException, MessagingException {
271 parse();
272
273 String boundary = "--" + contentType.getParameter("boundary");
274
275 for (int i = 0; i < parts.size(); i++) {
276 OutputUtil.writeln(boundary, os); // put out boundary
277 getBodyPart(i).writeTo(os);
278 OutputUtil.writeln(os); // put out empty line
279 }
280
281 // put out last boundary
282 OutputUtil.writeAsAscii(boundary, os);
283 OutputUtil.writeAsAscii("--", os);
284 os.flush();
285 }
286
287 /**
288 * Parse the InputStream from our DataSource, constructing the
289 * appropriate MimeBodyParts. The <code>parsed</code> flag is
290 * set to true, and if true on entry nothing is done. This
291 * method is called by all other methods that need data for
292 * the body parts, to make sure the data has been parsed.
293 *
294 * @since JavaMail 1.2
295 */
296 protected void parse() throws MessagingException {
297 if (parsed)
298 return;
299
300 InputStream in;
301 SharedInputStream sin = null;
302 long start = 0, end = 0;
303 boolean foundClosingBoundary = false;
304
305 try {
306 in = ds.getInputStream();
307 if (!(in instanceof ByteArrayInputStream) &&
308 !(in instanceof BufferedInputStream) &&
309 !(in instanceof SharedInputStream))
310 in = new BufferedInputStream(in);
311 } catch (Exception ex) {
312 throw new MessagingException("No inputstream from datasource");
313 }
473 } catch (IOException ioex) {
474 throw new MessagingException("IO Error", ioex);
475 } finally {
476 if (buf != null)
477 buf.close();
478 }
479
480 if (!ignoreMissingEndBoundary && !foundClosingBoundary && sin== null) {
481 throw new MessagingException("Missing End Boundary for Mime Package : EOF while skipping headers");
482 }
483 parsed = true;
484 }
485
486 /**
487 * Create and return an InternetHeaders object that loads the
488 * headers from the given InputStream. Subclasses can override
489 * this method to return a subclass of InternetHeaders, if
490 * necessary. This implementation simply constructs and returns
491 * an InternetHeaders object.
492 *
493 * @param is the InputStream to read the headers from
494 * @exception MessagingException
495 * @since JavaMail 1.2
496 */
497 protected InternetHeaders createInternetHeaders(InputStream is)
498 throws MessagingException {
499 return new InternetHeaders(is);
500 }
501
502 /**
503 * Create and return a MimeBodyPart object to represent a
504 * body part parsed from the InputStream. Subclasses can override
505 * this method to return a subclass of MimeBodyPart, if
506 * necessary. This implementation simply constructs and returns
507 * a MimeBodyPart object.
508 *
509 * @param headers the headers for the body part
510 * @param content the content of the body part
511 * @since JavaMail 1.2
512 */
513 protected MimeBodyPart createMimeBodyPart(InternetHeaders headers, byte[] content, int len) {
514 return new MimeBodyPart(headers, content,len);
515 }
516
517 /**
518 * Create and return a MimeBodyPart object to represent a
519 * body part parsed from the InputStream. Subclasses can override
520 * this method to return a subclass of MimeBodyPart, if
521 * necessary. This implementation simply constructs and returns
522 * a MimeBodyPart object.
523 *
524 * @param is InputStream containing the body part
525 * @exception MessagingException
526 * @since JavaMail 1.2
527 */
528 protected MimeBodyPart createMimeBodyPart(InputStream is) throws MessagingException {
529 return new MimeBodyPart(is);
530 }
531
532 /**
533 * Setup this MimeMultipart object from the given MultipartDataSource. <p>
534 *
535 * The method adds the MultipartDataSource's MimeBodyPart
536 * objects into this MimeMultipart. This MimeMultipart's contentType is
537 * set to that of the MultipartDataSource. <p>
538 *
539 * This method is typically used in those cases where one
540 * has a multipart data source that has already been pre-parsed into
541 * the individual body parts (for example, an IMAP datasource), but
542 * needs to create an appropriate MimeMultipart subclass that represents
543 * a specific multipart subtype.
544 *
545 * @param mp MimeMultipart datasource
546 */
547
548 protected void setMultipartDataSource(MultipartDataSource mp)
549 throws MessagingException {
550 contentType = new ContentType(mp.getContentType());
551
552 int count = mp.getCount();
553 for (int i = 0; i < count; i++)
554 addBodyPart(mp.getBodyPart(i));
555 }
556
557 /**
558 * Return the content-type of this MimeMultipart. <p>
559 *
560 * This implementation just returns the value of the
561 * <code>contentType</code> field.
562 *
563 * @return content-type
564 * @see #contentType
565 */
566 public ContentType getContentType() {
567 return contentType;
|
1 /*
2 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
116 /**
117 * Default constructor. An empty MimeMultipart object
118 * is created. Its content type is set to "multipart/mixed".
119 * A unique boundary string is generated and this string is
120 * setup as the "boundary" parameter for the
121 * <code>contentType</code> field. <p>
122 *
123 * MimeBodyParts may be added later.
124 */
125 public MimeMultipart() {
126 this("mixed");
127 }
128
129 /**
130 * Construct a MimeMultipart object of the given subtype.
131 * A unique boundary string is generated and this string is
132 * setup as the "boundary" parameter for the
133 * <code>contentType</code> field. <p>
134 *
135 * MimeBodyParts may be added later.
136 * @param subtype subtype.
137 */
138 public MimeMultipart(String subtype) {
139 //super();
140 /*
141 * Compute a boundary string.
142 */
143 String boundary = UniqueValue.getUniqueBoundaryValue();
144 contentType = new ContentType("multipart", subtype, null);
145 contentType.setParameter("boundary", boundary);
146 }
147
148 /**
149 * Constructs a MimeMultipart object and its bodyparts from the
150 * given DataSource. <p>
151 *
152 * This constructor handles as a special case the situation where the
153 * given DataSource is a MultipartDataSource object.
154 *
155 * Otherwise, the DataSource is assumed to provide a MIME multipart
156 * byte stream. The <code>parsed</code> flag is set to false. When
157 * the data for the body parts are needed, the parser extracts the
158 * "boundary" parameter from the content type of this DataSource,
159 * skips the 'preamble' and reads bytes till the terminating
160 * boundary and creates MimeBodyParts for each part of the stream.
161 *
162 * @param ds DataSource, can be a MultipartDataSource
163 * @param ct
164 * This must be the same information as {@link DataSource#getContentType()}.
165 * All the callers of this method seem to have this object handy, so
166 * for performance reason this method accepts it. Can be null.
167 *
168 * @exception MessagingException in case of error
169 */
170 public MimeMultipart(DataSource ds, ContentType ct) throws MessagingException {
171 // 'ds' was not a MultipartDataSource, we have
172 // to parse this ourself.
173 parsed = false;
174 this.ds = ds;
175 if (ct==null)
176 contentType = new ContentType(ds.getContentType());
177 else
178 contentType = ct;
179 }
180
181 /**
182 * Set the subtype. This method should be invoked only on a new
183 * MimeMultipart object created by the client. The default subtype
184 * of such a multipart object is "mixed". <p>
185 *
186 * @param subtype Subtype
187 */
188 public void setSubType(String subtype) {
189 contentType.setSubType(subtype);
190 }
191
192 /**
193 * Return the number of enclosed MimeBodyPart objects.
194 *
195 * @return number of parts.
196 * @throws MessagingException in case of error.
197 */
198 public int getCount() throws MessagingException {
199 parse();
200 if (parts == null)
201 return 0;
202
203 return parts.size();
204 }
205
206 /**
207 * Get the specified MimeBodyPart. BodyParts are numbered starting at 0.
208 *
209 * @param index the index of the desired MimeBodyPart.
210 * @return the MimeBodyPart.
211 * @exception MessagingException if no such MimeBodyPart exists
212 */
213 public MimeBodyPart getBodyPart(int index)
214 throws MessagingException {
215 parse();
216 if (parts == null)
217 throw new IndexOutOfBoundsException("No such BodyPart");
218
219 return parts.get(index);
220 }
221
222 /**
223 * Get the MimeBodyPart referred to by the given ContentID (CID).
224 * Returns null if the part is not found.
225 *
226 * @param CID the ContentID of the desired part
227 * @return the MimeBodyPart
228 * @exception MessagingException if no such MimeBodyPart exists.
229 */
230 public MimeBodyPart getBodyPart(String CID)
231 throws MessagingException {
232 parse();
233
234 int count = getCount();
235 for (int i = 0; i < count; i++) {
236 MimeBodyPart part = getBodyPart(i);
237 String s = part.getContentID();
238 // Old versions of AXIS2 put angle brackets around the content
239 // id but not the start param
240 String sNoAngle = (s!= null) ? s.replaceFirst("^<", "").replaceFirst(">$", "")
241 :null;
242 if (s != null && (s.equals(CID) || CID.equals(sNoAngle)))
243 return part;
244 }
245 return null;
246 }
247
248 /**
249 * Update headers. The default implementation here just
250 * calls the <code>updateHeaders</code> method on each of its
251 * children BodyParts. <p>
252 *
253 * Note that the boundary parameter is already set up when
254 * a new and empty MimeMultipart object is created. <p>
255 *
256 * This method is called when the <code>saveChanges</code>
257 * method is invoked on the Message object containing this
258 * MimeMultipart. This is typically done as part of the Message
259 * send process, however note that a client is free to call
260 * it any number of times. So if the header updating process is
261 * expensive for a specific MimeMultipart subclass, then it
262 * might itself want to track whether its internal state actually
263 * did change, and do the header updating only if necessary.
264 *
265 * @exception MessagingException in case of error.
266 */
267 protected void updateHeaders() throws MessagingException {
268 for (int i = 0; i < parts.size(); i++)
269 parts.get(i).updateHeaders();
270 }
271
272 /**
273 * Iterates through all the parts and outputs each Mime part
274 * separated by a boundary.
275 *
276 * @param os output stream.
277 *
278 * @exception IOException if an I/O Error occurs.
279 * @exception MessagingException in case of error.
280 */
281 public void writeTo(OutputStream os)
282 throws IOException, MessagingException {
283 parse();
284
285 String boundary = "--" + contentType.getParameter("boundary");
286
287 for (int i = 0; i < parts.size(); i++) {
288 OutputUtil.writeln(boundary, os); // put out boundary
289 getBodyPart(i).writeTo(os);
290 OutputUtil.writeln(os); // put out empty line
291 }
292
293 // put out last boundary
294 OutputUtil.writeAsAscii(boundary, os);
295 OutputUtil.writeAsAscii("--", os);
296 os.flush();
297 }
298
299 /**
300 * Parse the InputStream from our DataSource, constructing the
301 * appropriate MimeBodyParts. The <code>parsed</code> flag is
302 * set to true, and if true on entry nothing is done. This
303 * method is called by all other methods that need data for
304 * the body parts, to make sure the data has been parsed.
305 *
306 * @exception MessagingException in case of error.
307 *
308 * @since JavaMail 1.2
309 */
310 protected void parse() throws MessagingException {
311 if (parsed)
312 return;
313
314 InputStream in;
315 SharedInputStream sin = null;
316 long start = 0, end = 0;
317 boolean foundClosingBoundary = false;
318
319 try {
320 in = ds.getInputStream();
321 if (!(in instanceof ByteArrayInputStream) &&
322 !(in instanceof BufferedInputStream) &&
323 !(in instanceof SharedInputStream))
324 in = new BufferedInputStream(in);
325 } catch (Exception ex) {
326 throw new MessagingException("No inputstream from datasource");
327 }
487 } catch (IOException ioex) {
488 throw new MessagingException("IO Error", ioex);
489 } finally {
490 if (buf != null)
491 buf.close();
492 }
493
494 if (!ignoreMissingEndBoundary && !foundClosingBoundary && sin== null) {
495 throw new MessagingException("Missing End Boundary for Mime Package : EOF while skipping headers");
496 }
497 parsed = true;
498 }
499
500 /**
501 * Create and return an InternetHeaders object that loads the
502 * headers from the given InputStream. Subclasses can override
503 * this method to return a subclass of InternetHeaders, if
504 * necessary. This implementation simply constructs and returns
505 * an InternetHeaders object.
506 *
507 * @param is the InputStream to read the headers from.
508 * @return headers.
509 * @exception MessagingException in case of error.
510 * @since JavaMail 1.2
511 */
512 protected InternetHeaders createInternetHeaders(InputStream is)
513 throws MessagingException {
514 return new InternetHeaders(is);
515 }
516
517 /**
518 * Create and return a MimeBodyPart object to represent a
519 * body part parsed from the InputStream. Subclasses can override
520 * this method to return a subclass of MimeBodyPart, if
521 * necessary. This implementation simply constructs and returns
522 * a MimeBodyPart object.
523 *
524 * @param headers the headers for the body part.
525 * @param content the content of the body part.
526 * @param len the content length.
527 * @return MimeBodyPart
528 * @since JavaMail 1.2
529 */
530 protected MimeBodyPart createMimeBodyPart(InternetHeaders headers, byte[] content, int len) {
531 return new MimeBodyPart(headers, content,len);
532 }
533
534 /**
535 * Create and return a MimeBodyPart object to represent a
536 * body part parsed from the InputStream. Subclasses can override
537 * this method to return a subclass of MimeBodyPart, if
538 * necessary. This implementation simply constructs and returns
539 * a MimeBodyPart object.
540 *
541 * @param is InputStream containing the body part.
542 * @return MimeBodyPart.
543 * @exception MessagingException in case of error.
544 * @since JavaMail 1.2
545 */
546 protected MimeBodyPart createMimeBodyPart(InputStream is) throws MessagingException {
547 return new MimeBodyPart(is);
548 }
549
550 /**
551 * Setup this MimeMultipart object from the given MultipartDataSource. <p>
552 *
553 * The method adds the MultipartDataSource's MimeBodyPart
554 * objects into this MimeMultipart. This MimeMultipart's contentType is
555 * set to that of the MultipartDataSource. <p>
556 *
557 * This method is typically used in those cases where one
558 * has a multipart data source that has already been pre-parsed into
559 * the individual body parts (for example, an IMAP datasource), but
560 * needs to create an appropriate MimeMultipart subclass that represents
561 * a specific multipart subtype.
562 *
563 * @param mp MimeMultipart datasource
564 * @exception MessagingException in case of error.
565 */
566 protected void setMultipartDataSource(MultipartDataSource mp)
567 throws MessagingException {
568 contentType = new ContentType(mp.getContentType());
569
570 int count = mp.getCount();
571 for (int i = 0; i < count; i++)
572 addBodyPart(mp.getBodyPart(i));
573 }
574
575 /**
576 * Return the content-type of this MimeMultipart. <p>
577 *
578 * This implementation just returns the value of the
579 * <code>contentType</code> field.
580 *
581 * @return content-type
582 * @see #contentType
583 */
584 public ContentType getContentType() {
585 return contentType;
|