~testplan-team/testplan/source-collection

« back to all changes in this revision

Viewing changes to javamail-1.4.3/mail/src/main/java/javax/mail/internet/MimeMultipart.java

  • Committer: edA-qa mort-ora-y
  • Date: 2010-11-26 18:56:28 UTC
  • Revision ID: eda-qa@disemia.com-20101126185628-elxvrs14srop28r2
adding javamail 1.4.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
 
5
 *
 
6
 * The contents of this file are subject to the terms of either the GNU
 
7
 * General Public License Version 2 only ("GPL") or the Common Development
 
8
 * and Distribution License("CDDL") (collectively, the "License").  You
 
9
 * may not use this file except in compliance with the License. You can obtain
 
10
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 
11
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 
12
 * language governing permissions and limitations under the License.
 
13
 *
 
14
 * When distributing the software, include this License Header Notice in each
 
15
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 
16
 * Sun designates this particular file as subject to the "Classpath" exception
 
17
 * as provided by Sun in the GPL Version 2 section of the License file that
 
18
 * accompanied this code.  If applicable, add the following below the License
 
19
 * Header, with the fields enclosed by brackets [] replaced by your own
 
20
 * identifying information: "Portions Copyrighted [year]
 
21
 * [name of copyright owner]"
 
22
 *
 
23
 * Contributor(s):
 
24
 *
 
25
 * If you wish your version of this file to be governed by only the CDDL or
 
26
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 
27
 * elects to include this software in this distribution under the [CDDL or GPL
 
28
 * Version 2] license."  If you don't indicate a single choice of license, a
 
29
 * recipient has the option to distribute your version of this file under
 
30
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 
31
 * its licensees as provided above.  However, if you add GPL Version 2 code
 
32
 * and therefore, elected the GPL Version 2 license, then the option applies
 
33
 * only if the new code is made subject to such option by the copyright
 
34
 * holder.
 
35
 */
 
36
 
 
37
package javax.mail.internet;
 
38
 
 
39
import javax.mail.*;
 
40
import javax.activation.*;
 
41
import java.util.*;
 
42
import java.io.*;
 
43
import com.sun.mail.util.LineOutputStream;
 
44
import com.sun.mail.util.LineInputStream;
 
45
import com.sun.mail.util.ASCIIUtility;
 
46
import com.sun.mail.util.PropUtil;
 
47
 
 
48
/**
 
49
 * The MimeMultipart class is an implementation of the abstract Multipart
 
50
 * class that uses MIME conventions for the multipart data. <p>
 
51
 *
 
52
 * A MimeMultipart is obtained from a MimePart whose primary type
 
53
 * is "multipart" (by invoking the part's <code>getContent()</code> method)
 
54
 * or it can be created by a client as part of creating a new MimeMessage. <p>
 
55
 *
 
56
 * The default multipart subtype is "mixed".  The other multipart
 
57
 * subtypes, such as "alternative", "related", and so on, can be
 
58
 * implemented as subclasses of MimeMultipart with additional methods
 
59
 * to implement the additional semantics of that type of multipart
 
60
 * content. The intent is that service providers, mail JavaBean writers
 
61
 * and mail clients will write many such subclasses and their Command
 
62
 * Beans, and will install them into the JavaBeans Activation
 
63
 * Framework, so that any JavaMail implementation and its clients can
 
64
 * transparently find and use these classes. Thus, a MIME multipart
 
65
 * handler is treated just like any other type handler, thereby
 
66
 * decoupling the process of providing multipart handlers from the
 
67
 * JavaMail API. Lacking these additional MimeMultipart subclasses,
 
68
 * all subtypes of MIME multipart data appear as MimeMultipart objects. <p>
 
69
 *
 
70
 * An application can directly construct a MIME multipart object of any
 
71
 * subtype by using the <code>MimeMultipart(String subtype)</code>
 
72
 * constructor.  For example, to create a "multipart/alternative" object,
 
73
 * use <code>new MimeMultipart("alternative")</code>. <p>
 
74
 *
 
75
 * The <code>mail.mime.multipart.ignoremissingendboundary</code>
 
76
 * property may be set to <code>false</code> to cause a
 
77
 * <code>MessagingException</code> to be thrown if the multipart
 
78
 * data does not end with the required end boundary line.  If this
 
79
 * property is set to <code>true</code> or not set, missing end
 
80
 * boundaries are not considered an error and the final body part
 
81
 * ends at the end of the data. <p>
 
82
 *
 
83
 * The <code>mail.mime.multipart.ignoremissingboundaryparameter</code>
 
84
 * System property may be set to <code>false</code> to cause a
 
85
 * <code>MessagingException</code> to be thrown if the Content-Type
 
86
 * of the MimeMultipart does not include a <code>boundary</code> parameter.
 
87
 * If this property is set to <code>true</code> or not set, the multipart
 
88
 * parsing code will look for a line that looks like a bounary line and
 
89
 * use that as the boundary separating the parts. <p>
 
90
 *
 
91
 * The current implementation also supports the following properties: <p>
 
92
 *
 
93
 * The <code>mail.mime.multipart.ignoreexistingboundaryparameter</code>
 
94
 * System property may be set to <code>true</code> to cause any boundary
 
95
 * to be ignored and instead search for a boundary line in the message
 
96
 * as with <code>mail.mime.multipart.ignoremissingboundaryparameter</code>. <p>
 
97
 *
 
98
 * Normally, when writing out a MimeMultipart that contains no body
 
99
 * parts, or when trying to parse a multipart message with no body parts,
 
100
 * a <code>MessagingException</code> is thrown.  The MIME spec does not allow
 
101
 * multipart content with no body parts.  The
 
102
 * <code>mail.mime.multipart.allowempty</code> System property may be set to
 
103
 * <code>true</code> to override this behavior.
 
104
 * When writing out such a MimeMultipart, a single empty part will be
 
105
 * included.  When reading such a multipart, a MimeMultipart will be created
 
106
 * with no body parts.
 
107
 *
 
108
 * @author  John Mani
 
109
 * @author  Bill Shannon
 
110
 * @author  Max Spivak
 
111
 */
 
112
 
 
113
public class MimeMultipart extends Multipart {
 
114
 
 
115
    /**
 
116
     * The DataSource supplying our InputStream.
 
117
     */
 
118
    protected DataSource ds = null;
 
119
 
 
120
    /**
 
121
     * Have we parsed the data from our InputStream yet?
 
122
     * Defaults to true; set to false when our constructor is
 
123
     * given a DataSource with an InputStream that we need to
 
124
     * parse.
 
125
     */
 
126
    protected boolean parsed = true;
 
127
 
 
128
    /**
 
129
     * Have we seen the final bounary line?
 
130
     */
 
131
    private boolean complete = true;
 
132
 
 
133
    /**
 
134
     * The MIME multipart preamble text, the text that
 
135
     * occurs before the first boundary line.
 
136
     */
 
137
    private String preamble = null;
 
138
 
 
139
    /**
 
140
     * Flags to control parsing, initialized from System properties
 
141
     * in the parse() method.
 
142
     */
 
143
    private boolean ignoreMissingEndBoundary = true;
 
144
    private boolean ignoreMissingBoundaryParameter = true;
 
145
    private boolean ignoreExistingBoundaryParameter = false;
 
146
    private boolean allowEmpty = false;
 
147
    private boolean bmparse = true;
 
148
 
 
149
    /**
 
150
     * Default constructor. An empty MimeMultipart object
 
151
     * is created. Its content type is set to "multipart/mixed".
 
152
     * A unique boundary string is generated and this string is
 
153
     * setup as the "boundary" parameter for the 
 
154
     * <code>contentType</code> field. <p>
 
155
     *
 
156
     * MimeBodyParts may be added later.
 
157
     */
 
158
    public MimeMultipart() {
 
159
        this("mixed");
 
160
    }
 
161
 
 
162
    /**
 
163
     * Construct a MimeMultipart object of the given subtype.
 
164
     * A unique boundary string is generated and this string is
 
165
     * setup as the "boundary" parameter for the 
 
166
     * <code>contentType</code> field. <p>
 
167
     *
 
168
     * MimeBodyParts may be added later.
 
169
     */
 
170
    public MimeMultipart(String subtype) {
 
171
        super();
 
172
        /*
 
173
         * Compute a boundary string.
 
174
         */
 
175
        String boundary = UniqueValue.getUniqueBoundaryValue();
 
176
        ContentType cType = new ContentType("multipart", subtype, null);
 
177
        cType.setParameter("boundary", boundary);
 
178
        contentType = cType.toString();
 
179
    }
 
180
 
 
181
    /**
 
182
     * Constructs a MimeMultipart object and its bodyparts from the 
 
183
     * given DataSource. <p>
 
184
     *
 
185
     * This constructor handles as a special case the situation where the
 
186
     * given DataSource is a MultipartDataSource object.  In this case, this
 
187
     * method just invokes the superclass (i.e., Multipart) constructor
 
188
     * that takes a MultipartDataSource object. <p>
 
189
     *
 
190
     * Otherwise, the DataSource is assumed to provide a MIME multipart 
 
191
     * byte stream.  The <code>parsed</code> flag is set to false.  When
 
192
     * the data for the body parts are needed, the parser extracts the
 
193
     * "boundary" parameter from the content type of this DataSource,
 
194
     * skips the 'preamble' and reads bytes till the terminating
 
195
     * boundary and creates MimeBodyParts for each part of the stream.
 
196
     *
 
197
     * @param   ds      DataSource, can be a MultipartDataSource
 
198
     */
 
199
    public MimeMultipart(DataSource ds) throws MessagingException {
 
200
        super();
 
201
 
 
202
        if (ds instanceof MessageAware) {
 
203
            MessageContext mc = ((MessageAware)ds).getMessageContext();
 
204
            setParent(mc.getPart());
 
205
        }
 
206
 
 
207
        if (ds instanceof MultipartDataSource) {
 
208
            // ask super to do this for us.
 
209
            setMultipartDataSource((MultipartDataSource)ds);
 
210
            return;
 
211
        }
 
212
 
 
213
        // 'ds' was not a MultipartDataSource, we have
 
214
        // to parse this ourself.
 
215
        parsed = false;
 
216
        this.ds = ds;
 
217
        contentType = ds.getContentType();
 
218
    }
 
219
 
 
220
    /**
 
221
     * Set the subtype. This method should be invoked only on a new
 
222
     * MimeMultipart object created by the client. The default subtype
 
223
     * of such a multipart object is "mixed". <p>
 
224
     *
 
225
     * @param   subtype         Subtype
 
226
     */
 
227
    public synchronized void setSubType(String subtype) 
 
228
                        throws MessagingException {
 
229
        ContentType cType = new ContentType(contentType);       
 
230
        cType.setSubType(subtype);
 
231
        contentType = cType.toString();
 
232
    }
 
233
 
 
234
    /**
 
235
     * Return the number of enclosed BodyPart objects.
 
236
     *
 
237
     * @return          number of parts
 
238
     */
 
239
    public synchronized int getCount() throws MessagingException {
 
240
        parse();
 
241
        return super.getCount();
 
242
    }
 
243
 
 
244
    /**
 
245
     * Get the specified BodyPart.  BodyParts are numbered starting at 0.
 
246
     *
 
247
     * @param index     the index of the desired BodyPart
 
248
     * @return          the Part
 
249
     * @exception       MessagingException if no such BodyPart exists
 
250
     */
 
251
    public synchronized BodyPart getBodyPart(int index) 
 
252
                        throws MessagingException {
 
253
        parse();
 
254
        return super.getBodyPart(index);
 
255
    }
 
256
 
 
257
    /**
 
258
     * Get the MimeBodyPart referred to by the given ContentID (CID). 
 
259
     * Returns null if the part is not found.
 
260
     *
 
261
     * @param  CID      the ContentID of the desired part
 
262
     * @return          the Part
 
263
     */
 
264
    public synchronized BodyPart getBodyPart(String CID) 
 
265
                        throws MessagingException {
 
266
        parse();
 
267
 
 
268
        int count = getCount();
 
269
        for (int i = 0; i < count; i++) {
 
270
           MimeBodyPart part = (MimeBodyPart)getBodyPart(i);
 
271
           String s = part.getContentID();
 
272
           if (s != null && s.equals(CID))
 
273
                return part;    
 
274
        }
 
275
        return null;
 
276
    }
 
277
 
 
278
    /**
 
279
     * Remove the specified part from the multipart message.
 
280
     * Shifts all the parts after the removed part down one.
 
281
     *
 
282
     * @param   part    The part to remove
 
283
     * @return          true if part removed, false otherwise
 
284
     * @exception       MessagingException if no such Part exists
 
285
     * @exception       IllegalWriteException if the underlying
 
286
     *                  implementation does not support modification
 
287
     *                  of existing values
 
288
     */
 
289
    public boolean removeBodyPart(BodyPart part) throws MessagingException {
 
290
        parse();
 
291
        return super.removeBodyPart(part);
 
292
    }
 
293
 
 
294
    /**
 
295
     * Remove the part at specified location (starting from 0).
 
296
     * Shifts all the parts after the removed part down one.
 
297
     *
 
298
     * @param   index   Index of the part to remove
 
299
     * @exception       MessagingException
 
300
     * @exception       IndexOutOfBoundsException if the given index
 
301
     *                  is out of range.
 
302
     * @exception       IllegalWriteException if the underlying
 
303
     *                  implementation does not support modification
 
304
     *                  of existing values
 
305
     */
 
306
    public void removeBodyPart(int index) throws MessagingException {
 
307
        parse();
 
308
        super.removeBodyPart(index);
 
309
    }
 
310
 
 
311
    /**
 
312
     * Adds a Part to the multipart.  The BodyPart is appended to 
 
313
     * the list of existing Parts.
 
314
     *
 
315
     * @param  part  The Part to be appended
 
316
     * @exception       MessagingException
 
317
     * @exception       IllegalWriteException if the underlying
 
318
     *                  implementation does not support modification
 
319
     *                  of existing values
 
320
     */
 
321
    public synchronized void addBodyPart(BodyPart part) 
 
322
                throws MessagingException {
 
323
        parse();
 
324
        super.addBodyPart(part);
 
325
    }
 
326
 
 
327
    /**
 
328
     * Adds a BodyPart at position <code>index</code>.
 
329
     * If <code>index</code> is not the last one in the list,
 
330
     * the subsequent parts are shifted up. If <code>index</code>
 
331
     * is larger than the number of parts present, the
 
332
     * BodyPart is appended to the end.
 
333
     *
 
334
     * @param  part  The BodyPart to be inserted
 
335
     * @param  index Location where to insert the part
 
336
     * @exception       MessagingException
 
337
     * @exception       IllegalWriteException if the underlying
 
338
     *                  implementation does not support modification
 
339
     *                  of existing values
 
340
     */
 
341
    public synchronized void addBodyPart(BodyPart part, int index) 
 
342
                                throws MessagingException {
 
343
        parse();
 
344
        super.addBodyPart(part, index);
 
345
    }
 
346
 
 
347
    /**
 
348
     * Return true if the final boundary line for this
 
349
     * multipart was seen.  When parsing multipart content,
 
350
     * this class will (by default) terminate parsing with
 
351
     * no error if the end of input is reached before seeing
 
352
     * the final multipart boundary line.  In such a case,
 
353
     * this method will return false.  (If the System property
 
354
     * "mail.mime.multipart.ignoremissingendboundary" is set to
 
355
     * false, parsing such a message will instead throw a
 
356
     * MessagingException.)
 
357
     *
 
358
     * @return  true if the final boundary line was seen
 
359
     * @since           JavaMail 1.4
 
360
     */
 
361
    public synchronized boolean isComplete() throws MessagingException {
 
362
        parse();
 
363
        return complete;
 
364
    }
 
365
 
 
366
    /**
 
367
     * Get the preamble text, if any, that appears before the
 
368
     * first body part of this multipart.  Some protocols,
 
369
     * such as IMAP, will not allow access to the preamble text.
 
370
     *
 
371
     * @return          the preamble text, or null if no preamble
 
372
     * @since           JavaMail 1.4
 
373
     */
 
374
    public synchronized String getPreamble() throws MessagingException {
 
375
        parse();
 
376
        return preamble;
 
377
    }
 
378
 
 
379
    /**
 
380
     * Set the preamble text to be included before the first
 
381
     * body part.  Applications should generally not include
 
382
     * any preamble text.  In some cases it may be helpful to
 
383
     * include preamble text with instructions for users of
 
384
     * pre-MIME software.  The preamble text should be complete
 
385
     * lines, including newlines.
 
386
     *
 
387
     * @param   preamble        the preamble text
 
388
     * @since           JavaMail 1.4
 
389
     */
 
390
    public synchronized void setPreamble(String preamble)
 
391
                                throws MessagingException {
 
392
        this.preamble = preamble;
 
393
    }
 
394
 
 
395
    /**
 
396
     * Update headers. The default implementation here just
 
397
     * calls the <code>updateHeaders</code> method on each of its
 
398
     * children BodyParts. <p>
 
399
     *
 
400
     * Note that the boundary parameter is already set up when
 
401
     * a new and empty MimeMultipart object is created. <p>
 
402
     *
 
403
     * This method is called when the <code>saveChanges</code>
 
404
     * method is invoked on the Message object containing this
 
405
     * Multipart. This is typically done as part of the Message
 
406
     * send process, however note that a client is free to call
 
407
     * it any number of times. So if the header updating process is 
 
408
     * expensive for a specific MimeMultipart subclass, then it
 
409
     * might itself want to track whether its internal state actually
 
410
     * did change, and do the header updating only if necessary.
 
411
     */
 
412
    protected synchronized void updateHeaders() throws MessagingException {
 
413
        parse();
 
414
        for (int i = 0; i < parts.size(); i++)
 
415
            ((MimeBodyPart)parts.elementAt(i)).updateHeaders();
 
416
    }
 
417
 
 
418
    /**
 
419
     * Iterates through all the parts and outputs each MIME part
 
420
     * separated by a boundary.
 
421
     */
 
422
    public synchronized void writeTo(OutputStream os)
 
423
                                throws IOException, MessagingException {
 
424
        parse();
 
425
        
 
426
        String boundary = "--" + 
 
427
                (new ContentType(contentType)).getParameter("boundary");
 
428
        LineOutputStream los = new LineOutputStream(os);
 
429
 
 
430
        // if there's a preamble, write it out
 
431
        if (preamble != null) {
 
432
            byte[] pb = ASCIIUtility.getBytes(preamble);
 
433
            los.write(pb);
 
434
            // make sure it ends with a newline
 
435
            if (pb.length > 0 &&
 
436
                    !(pb[pb.length-1] == '\r' || pb[pb.length-1] == '\n')) {
 
437
                los.writeln();
 
438
            }
 
439
            // XXX - could force a blank line before start boundary
 
440
        }
 
441
 
 
442
        if (parts.size() == 0) {
 
443
            if (allowEmpty) {
 
444
                // write out a single empty body part
 
445
                los.writeln(boundary); // put out boundary
 
446
                los.writeln(); // put out empty line
 
447
            } else {
 
448
                throw new MessagingException("Empty multipart: " + contentType);
 
449
            }
 
450
        } else {
 
451
            for (int i = 0; i < parts.size(); i++) {
 
452
                los.writeln(boundary); // put out boundary
 
453
                ((MimeBodyPart)parts.elementAt(i)).writeTo(os);
 
454
                los.writeln(); // put out empty line
 
455
            }
 
456
        }
 
457
 
 
458
        // put out last boundary
 
459
        los.writeln(boundary + "--");
 
460
    }
 
461
 
 
462
    /**
 
463
     * Parse the InputStream from our DataSource, constructing the
 
464
     * appropriate MimeBodyParts.  The <code>parsed</code> flag is
 
465
     * set to true, and if true on entry nothing is done.  This
 
466
     * method is called by all other methods that need data for
 
467
     * the body parts, to make sure the data has been parsed.
 
468
     *
 
469
     * @since   JavaMail 1.2
 
470
     */
 
471
    protected synchronized void parse() throws MessagingException {
 
472
        if (parsed)
 
473
            return;
 
474
 
 
475
        // read properties that control parsing
 
476
 
 
477
        // default to true
 
478
        ignoreMissingEndBoundary = PropUtil.getBooleanSystemProperty(
 
479
            "mail.mime.multipart.ignoremissingendboundary", true);
 
480
        // default to true
 
481
        ignoreMissingBoundaryParameter = PropUtil.getBooleanSystemProperty(
 
482
            "mail.mime.multipart.ignoremissingboundaryparameter", true);
 
483
        // default to false
 
484
        ignoreExistingBoundaryParameter = PropUtil.getBooleanSystemProperty(
 
485
            "mail.mime.multipart.ignoreexistingboundaryparameter", false);
 
486
        // default to false
 
487
        allowEmpty = PropUtil.getBooleanSystemProperty(
 
488
            "mail.mime.multipart.allowempty", false);
 
489
        // default to true
 
490
        bmparse = PropUtil.getBooleanSystemProperty(
 
491
            "mail.mime.multipart.bmparse", true);
 
492
 
 
493
        if (bmparse) {
 
494
            parsebm();
 
495
            return;
 
496
        }
 
497
 
 
498
        InputStream in = null;
 
499
        SharedInputStream sin = null;
 
500
        long start = 0, end = 0;
 
501
 
 
502
        try {
 
503
            in = ds.getInputStream();
 
504
            if (!(in instanceof ByteArrayInputStream) &&
 
505
                !(in instanceof BufferedInputStream) &&
 
506
                !(in instanceof SharedInputStream))
 
507
                in = new BufferedInputStream(in);
 
508
        } catch (Exception ex) {
 
509
            throw new MessagingException("No inputstream from datasource", ex);
 
510
        }
 
511
        if (in instanceof SharedInputStream)
 
512
            sin = (SharedInputStream)in;
 
513
 
 
514
        ContentType cType = new ContentType(contentType);
 
515
        String boundary = null;
 
516
        if (!ignoreExistingBoundaryParameter) {
 
517
            String bp = cType.getParameter("boundary");
 
518
            if (bp != null)
 
519
                boundary = "--" + bp;
 
520
        }
 
521
        if (boundary == null && !ignoreMissingBoundaryParameter &&
 
522
                !ignoreExistingBoundaryParameter)
 
523
            throw new MessagingException("Missing boundary parameter");
 
524
 
 
525
        try {
 
526
            // Skip and save the preamble
 
527
            LineInputStream lin = new LineInputStream(in);
 
528
            StringBuffer preamblesb = null;
 
529
            String line;
 
530
            String lineSeparator = null;
 
531
            while ((line = lin.readLine()) != null) {
 
532
                /*
 
533
                 * Strip trailing whitespace.  Can't use trim method
 
534
                 * because it's too aggressive.  Some bogus MIME
 
535
                 * messages will include control characters in the
 
536
                 * boundary string.
 
537
                 */
 
538
                int i;
 
539
                for (i = line.length() - 1; i >= 0; i--) {
 
540
                    char c = line.charAt(i);
 
541
                    if (!(c == ' ' || c == '\t'))
 
542
                        break;
 
543
                }
 
544
                line = line.substring(0, i + 1);
 
545
                if (boundary != null) {
 
546
                    if (line.equals(boundary))
 
547
                        break;
 
548
                    if (line.length() == boundary.length() + 2 &&
 
549
                            line.startsWith(boundary) && line.endsWith("--")) {
 
550
                        line = null;    // signal end of multipart
 
551
                        break;
 
552
                    }
 
553
                } else {
 
554
                    /*
 
555
                     * Boundary hasn't been defined, does this line
 
556
                     * look like a boundary?  If so, assume it is
 
557
                     * the boundary and save it.
 
558
                     */
 
559
                    if (line.startsWith("--")) {
 
560
                        if (line.endsWith("--")) {
 
561
                            /*
 
562
                             * The first boundary-like line we find is
 
563
                             * probably *not* the end-of-multipart boundary
 
564
                             * line.  More likely it's a line full of dashes
 
565
                             * in the preamble text.  Just keep reading.
 
566
                             */
 
567
                        } else {
 
568
                            boundary = line;
 
569
                            break;
 
570
                        }
 
571
                    }
 
572
                }
 
573
 
 
574
                // save the preamble after skipping blank lines
 
575
                if (line.length() > 0) {
 
576
                    // if we haven't figured out what the line separator
 
577
                    // is, do it now
 
578
                    if (lineSeparator == null) {
 
579
                        try {
 
580
                            lineSeparator =
 
581
                                System.getProperty("line.separator", "\n");
 
582
                        } catch (SecurityException ex) {
 
583
                            lineSeparator = "\n";
 
584
                        }
 
585
                    }
 
586
                    // accumulate the preamble
 
587
                    if (preamblesb == null)
 
588
                        preamblesb = new StringBuffer(line.length() + 2);
 
589
                    preamblesb.append(line).append(lineSeparator);
 
590
                }
 
591
            }
 
592
 
 
593
            if (preamblesb != null)
 
594
                preamble = preamblesb.toString();
 
595
 
 
596
            if (line == null) {
 
597
                if (allowEmpty)
 
598
                    return;
 
599
                else
 
600
                    throw new MessagingException("Missing start boundary");
 
601
            }
 
602
 
 
603
            // save individual boundary bytes for easy comparison later
 
604
            byte[] bndbytes = ASCIIUtility.getBytes(boundary);
 
605
            int bl = bndbytes.length;
 
606
            
 
607
            /*
 
608
             * Read and process body parts until we see the
 
609
             * terminating boundary line (or EOF).
 
610
             */
 
611
            boolean done = false;
 
612
        getparts:
 
613
            while (!done) {
 
614
                InternetHeaders headers = null;
 
615
                if (sin != null) {
 
616
                    start = sin.getPosition();
 
617
                    // skip headers
 
618
                    while ((line = lin.readLine()) != null && line.length() > 0)
 
619
                        ;
 
620
                    if (line == null) {
 
621
                        if (!ignoreMissingEndBoundary)
 
622
                            throw new MessagingException(
 
623
                                        "missing multipart end boundary");
 
624
                        // assume there's just a missing end boundary
 
625
                        complete = false;
 
626
                        break getparts;
 
627
                    }
 
628
                } else {
 
629
                    // collect the headers for this body part
 
630
                    headers = createInternetHeaders(in);
 
631
                }
 
632
 
 
633
                if (!in.markSupported())
 
634
                    throw new MessagingException("Stream doesn't support mark");
 
635
 
 
636
                ByteArrayOutputStream buf = null;
 
637
                // if we don't have a shared input stream, we copy the data
 
638
                if (sin == null)
 
639
                    buf = new ByteArrayOutputStream();
 
640
                else
 
641
                    end = sin.getPosition();
 
642
                int b;
 
643
                boolean bol = true;    // beginning of line flag
 
644
                // the two possible end of line characters
 
645
                int eol1 = -1, eol2 = -1;
 
646
 
 
647
                /*
 
648
                 * Read and save the content bytes in buf.
 
649
                 */
 
650
                for (;;) {
 
651
                    if (bol) {
 
652
                        /*
 
653
                         * At the beginning of a line, check whether the
 
654
                         * next line is a boundary.
 
655
                         */
 
656
                        int i;
 
657
                        in.mark(bl + 4 + 1000); // bnd + "--\r\n" + lots of LWSP
 
658
                        // read bytes, matching against the boundary
 
659
                        for (i = 0; i < bl; i++)
 
660
                            if (in.read() != (bndbytes[i] & 0xff))
 
661
                                break;
 
662
                        if (i == bl) {
 
663
                            // matched the boundary, check for last boundary
 
664
                            int b2 = in.read();
 
665
                            if (b2 == '-') {
 
666
                                if (in.read() == '-') {
 
667
                                    complete = true;
 
668
                                    done = true;
 
669
                                    break;      // ignore trailing text
 
670
                                }
 
671
                            }
 
672
                            // skip linear whitespace
 
673
                            while (b2 == ' ' || b2 == '\t')
 
674
                                b2 = in.read();
 
675
                            // check for end of line
 
676
                            if (b2 == '\n')
 
677
                                break;  // got it!  break out of the loop
 
678
                            if (b2 == '\r') {
 
679
                                in.mark(1);
 
680
                                if (in.read() != '\n')
 
681
                                    in.reset();
 
682
                                break;  // got it!  break out of the loop
 
683
                            }
 
684
                        }
 
685
                        // failed to match, reset and proceed normally
 
686
                        in.reset();
 
687
 
 
688
                        // if this is not the first line, write out the
 
689
                        // end of line characters from the previous line
 
690
                        if (buf != null && eol1 != -1) {
 
691
                            buf.write(eol1);
 
692
                            if (eol2 != -1)
 
693
                                buf.write(eol2);
 
694
                            eol1 = eol2 = -1;
 
695
                        }
 
696
                    }
 
697
 
 
698
                    // read the next byte
 
699
                    if ((b = in.read()) < 0) {
 
700
                        if (!ignoreMissingEndBoundary)
 
701
                            throw new MessagingException(
 
702
                                        "missing multipart end boundary");
 
703
                        complete = false;
 
704
                        done = true;
 
705
                        break;
 
706
                    }
 
707
 
 
708
                    /*
 
709
                     * If we're at the end of the line, save the eol characters
 
710
                     * to be written out before the beginning of the next line.
 
711
                     */
 
712
                    if (b == '\r' || b == '\n') {
 
713
                        bol = true;
 
714
                        if (sin != null)
 
715
                            end = sin.getPosition() - 1;
 
716
                        eol1 = b;
 
717
                        if (b == '\r') {
 
718
                            in.mark(1);
 
719
                            if ((b = in.read()) == '\n')
 
720
                                eol2 = b;
 
721
                            else
 
722
                                in.reset();
 
723
                        }
 
724
                    } else {
 
725
                        bol = false;
 
726
                        if (buf != null)
 
727
                            buf.write(b);
 
728
                    }
 
729
                }
 
730
 
 
731
                /*
 
732
                 * Create a MimeBody element to represent this body part.
 
733
                 */
 
734
                MimeBodyPart part;
 
735
                if (sin != null)
 
736
                    part = createMimeBodyPart(sin.newStream(start, end));
 
737
                else
 
738
                    part = createMimeBodyPart(headers, buf.toByteArray());
 
739
                super.addBodyPart(part);
 
740
            }
 
741
        } catch (IOException ioex) {
 
742
            throw new MessagingException("IO Error", ioex);
 
743
        } finally {
 
744
            try {
 
745
                in.close();
 
746
            } catch (IOException cex) {
 
747
                // ignore
 
748
            }
 
749
        }
 
750
 
 
751
        parsed = true;
 
752
    }
 
753
 
 
754
    /**
 
755
     * Parse the InputStream from our DataSource, constructing the
 
756
     * appropriate MimeBodyParts.  The <code>parsed</code> flag is
 
757
     * set to true, and if true on entry nothing is done.  This
 
758
     * method is called by all other methods that need data for
 
759
     * the body parts, to make sure the data has been parsed.
 
760
     *
 
761
     * @since   JavaMail 1.2
 
762
     */
 
763
    /*
 
764
     * Boyer-Moore version of parser.  Keep both versions around
 
765
     * until we're sure this new one works.
 
766
     */
 
767
    private synchronized void parsebm() throws MessagingException {
 
768
        if (parsed)
 
769
            return;
 
770
        
 
771
        InputStream in = null;
 
772
        SharedInputStream sin = null;
 
773
        long start = 0, end = 0;
 
774
 
 
775
        try {
 
776
            in = ds.getInputStream();
 
777
            if (!(in instanceof ByteArrayInputStream) &&
 
778
                !(in instanceof BufferedInputStream) &&
 
779
                !(in instanceof SharedInputStream))
 
780
                in = new BufferedInputStream(in);
 
781
        } catch (Exception ex) {
 
782
            throw new MessagingException("No inputstream from datasource", ex);
 
783
        }
 
784
        if (in instanceof SharedInputStream)
 
785
            sin = (SharedInputStream)in;
 
786
 
 
787
        ContentType cType = new ContentType(contentType);
 
788
        String boundary = null;
 
789
        if (!ignoreExistingBoundaryParameter) {
 
790
            String bp = cType.getParameter("boundary");
 
791
            if (bp != null)
 
792
                boundary = "--" + bp;
 
793
        }
 
794
        if (boundary == null && !ignoreMissingBoundaryParameter &&
 
795
                !ignoreExistingBoundaryParameter)
 
796
            throw new MessagingException("Missing boundary parameter");
 
797
 
 
798
        try {
 
799
            // Skip and save the preamble
 
800
            LineInputStream lin = new LineInputStream(in);
 
801
            StringBuffer preamblesb = null;
 
802
            String line;
 
803
            String lineSeparator = null;
 
804
            while ((line = lin.readLine()) != null) {
 
805
                /*
 
806
                 * Strip trailing whitespace.  Can't use trim method
 
807
                 * because it's too aggressive.  Some bogus MIME
 
808
                 * messages will include control characters in the
 
809
                 * boundary string.
 
810
                 */
 
811
                int i;
 
812
                for (i = line.length() - 1; i >= 0; i--) {
 
813
                    char c = line.charAt(i);
 
814
                    if (!(c == ' ' || c == '\t'))
 
815
                        break;
 
816
                }
 
817
                line = line.substring(0, i + 1);
 
818
                if (boundary != null) {
 
819
                    if (line.equals(boundary))
 
820
                        break;
 
821
                    if (line.length() == boundary.length() + 2 &&
 
822
                            line.startsWith(boundary) && line.endsWith("--")) {
 
823
                        line = null;    // signal end of multipart
 
824
                        break;
 
825
                    }
 
826
                } else {
 
827
                    /*
 
828
                     * Boundary hasn't been defined, does this line
 
829
                     * look like a boundary?  If so, assume it is
 
830
                     * the boundary and save it.
 
831
                     */
 
832
                    if (line.startsWith("--")) {
 
833
                        if (line.endsWith("--")) {
 
834
                            /*
 
835
                             * The first boundary-like line we find is
 
836
                             * probably *not* the end-of-multipart boundary
 
837
                             * line.  More likely it's a line full of dashes
 
838
                             * in the preamble text.  Just keep reading.
 
839
                             */
 
840
                        } else {
 
841
                            boundary = line;
 
842
                            break;
 
843
                        }
 
844
                    }
 
845
                }
 
846
 
 
847
                // save the preamble after skipping blank lines
 
848
                if (line.length() > 0) {
 
849
                    // if we haven't figured out what the line separator
 
850
                    // is, do it now
 
851
                    if (lineSeparator == null) {
 
852
                        try {
 
853
                            lineSeparator =
 
854
                                System.getProperty("line.separator", "\n");
 
855
                        } catch (SecurityException ex) {
 
856
                            lineSeparator = "\n";
 
857
                        }
 
858
                    }
 
859
                    // accumulate the preamble
 
860
                    if (preamblesb == null)
 
861
                        preamblesb = new StringBuffer(line.length() + 2);
 
862
                    preamblesb.append(line).append(lineSeparator);
 
863
                }
 
864
            }
 
865
 
 
866
            if (preamblesb != null)
 
867
                preamble = preamblesb.toString();
 
868
 
 
869
            if (line == null) {
 
870
                if (allowEmpty)
 
871
                    return;
 
872
                else
 
873
                    throw new MessagingException("Missing start boundary");
 
874
            }
 
875
 
 
876
            // save individual boundary bytes for comparison later
 
877
            byte[] bndbytes = ASCIIUtility.getBytes(boundary);
 
878
            int bl = bndbytes.length;
 
879
 
 
880
            /*
 
881
             * Compile Boyer-Moore parsing tables.
 
882
             */
 
883
 
 
884
            // initialize Bad Character Shift table
 
885
            int[] bcs = new int[256];
 
886
            for (int i = 0; i < bl; i++)
 
887
                bcs[bndbytes[i]] = i + 1;
 
888
 
 
889
            // initialize Good Suffix Shift table
 
890
            int[] gss = new int[bl];
 
891
        NEXT:
 
892
            for (int i = bl; i > 0; i--) {
 
893
                int j;  // the beginning index of the suffix being considered
 
894
                for (j = bl - 1; j >= i; j--) {
 
895
                    // Testing for good suffix
 
896
                    if (bndbytes[j] == bndbytes[j - i]) {
 
897
                        // bndbytes[j..len] is a good suffix
 
898
                        gss[j - 1] = i;
 
899
                    } else {
 
900
                       // No match. The array has already been
 
901
                       // filled up with correct values before.
 
902
                       continue NEXT;
 
903
                    }
 
904
                }
 
905
                while (j > 0)
 
906
                    gss[--j] = i;
 
907
            }
 
908
            gss[bl - 1] = 1;
 
909
 
 
910
            /*
 
911
             * Read and process body parts until we see the
 
912
             * terminating boundary line (or EOF).
 
913
             */
 
914
            boolean done = false;
 
915
        getparts:
 
916
            while (!done) {
 
917
                InternetHeaders headers = null;
 
918
                if (sin != null) {
 
919
                    start = sin.getPosition();
 
920
                    // skip headers
 
921
                    while ((line = lin.readLine()) != null && line.length() > 0)
 
922
                        ;
 
923
                    if (line == null) {
 
924
                        if (!ignoreMissingEndBoundary)
 
925
                            throw new MessagingException(
 
926
                                        "missing multipart end boundary");
 
927
                        // assume there's just a missing end boundary
 
928
                        complete = false;
 
929
                        break getparts;
 
930
                    }
 
931
                } else {
 
932
                    // collect the headers for this body part
 
933
                    headers = createInternetHeaders(in);
 
934
                }
 
935
 
 
936
                if (!in.markSupported())
 
937
                    throw new MessagingException("Stream doesn't support mark");
 
938
 
 
939
                ByteArrayOutputStream buf = null;
 
940
                // if we don't have a shared input stream, we copy the data
 
941
                if (sin == null)
 
942
                    buf = new ByteArrayOutputStream();
 
943
                else
 
944
                    end = sin.getPosition();
 
945
                int b;
 
946
 
 
947
                /*
 
948
                 * These buffers contain the bytes we're checking
 
949
                 * for a match.  inbuf is the current buffer and
 
950
                 * previnbuf is the previous buffer.  We need the
 
951
                 * previous buffer to check that we're preceeded
 
952
                 * by an EOL.
 
953
                 */
 
954
                // XXX - a smarter algorithm would use a sliding window
 
955
                //       over a larger buffer
 
956
                byte[] inbuf = new byte[bl];
 
957
                byte[] previnbuf = new byte[bl];
 
958
                int inSize = 0;         // number of valid bytes in inbuf
 
959
                int prevSize = 0;       // number of valid bytes in previnbuf
 
960
                int eolLen;
 
961
                boolean first = true;
 
962
 
 
963
                /*
 
964
                 * Read and save the content bytes in buf.
 
965
                 */
 
966
                for (;;) {
 
967
                    in.mark(bl + 4 + 1000); // bnd + "--\r\n" + lots of LWSP
 
968
                    eolLen = 0;
 
969
                    inSize = readFully(in, inbuf, 0, bl);
 
970
                    if (inSize < bl) {
 
971
                        // hit EOF
 
972
                        if (!ignoreMissingEndBoundary)
 
973
                            throw new MessagingException(
 
974
                                        "missing multipart end boundary");
 
975
                        if (sin != null)
 
976
                            end = sin.getPosition();
 
977
                        complete = false;
 
978
                        done = true;
 
979
                        break;
 
980
                    }
 
981
                    // check whether inbuf contains a boundary string
 
982
                    int i;
 
983
                    for (i = bl - 1; i >= 0; i--) {
 
984
                        if (inbuf[i] != bndbytes[i])
 
985
                            break;
 
986
                    }
 
987
                    if (i < 0) {        // matched all bytes
 
988
                        eolLen = 0;
 
989
                        if (!first) {
 
990
                            // working backwards, find out if we were preceeded
 
991
                            // by an EOL, and if so find its length
 
992
                            b = previnbuf[prevSize - 1];
 
993
                            if (b == '\r' || b == '\n') {
 
994
                                eolLen = 1;
 
995
                                if (b == '\n' && prevSize >= 2) {
 
996
                                    b = previnbuf[prevSize - 2];
 
997
                                    if (b == '\r')
 
998
                                        eolLen = 2;
 
999
                                }
 
1000
                            }
 
1001
                        }
 
1002
                        if (first || eolLen > 0) {      // yes, preceed by EOL
 
1003
                            if (sin != null) {
 
1004
                                // update "end", in case this really is
 
1005
                                // a valid boundary
 
1006
                                end = sin.getPosition() - bl - eolLen;
 
1007
                            }
 
1008
                            // matched the boundary, check for last boundary
 
1009
                            int b2 = in.read();
 
1010
                            if (b2 == '-') {
 
1011
                                if (in.read() == '-') {
 
1012
                                    complete = true;
 
1013
                                    done = true;
 
1014
                                    break;      // ignore trailing text
 
1015
                                }
 
1016
                            }
 
1017
                            // skip linear whitespace
 
1018
                            while (b2 == ' ' || b2 == '\t')
 
1019
                                b2 = in.read();
 
1020
                            // check for end of line
 
1021
                            if (b2 == '\n')
 
1022
                                break;  // got it!  break out of the loop
 
1023
                            if (b2 == '\r') {
 
1024
                                in.mark(1);
 
1025
                                if (in.read() != '\n')
 
1026
                                    in.reset();
 
1027
                                break;  // got it!  break out of the loop
 
1028
                            }
 
1029
                        }
 
1030
                        i = 0;
 
1031
                    }
 
1032
 
 
1033
                    /*
 
1034
                     * Get here if boundary didn't match,
 
1035
                     * wasn't preceeded by EOL, or wasn't
 
1036
                     * followed by whitespace or EOL.
 
1037
                     */
 
1038
 
 
1039
                    // compute how many bytes we can skip
 
1040
                    int skip = Math.max(i + 1 - bcs[inbuf[i] & 0x7f], gss[i]);
 
1041
                    // want to keep at least two characters
 
1042
                    if (skip < 2) {
 
1043
                        // only skipping one byte, save one byte
 
1044
                        // from previous buffer as well
 
1045
                        // first, write out bytes we're done with
 
1046
                        if (sin == null && prevSize > 1)
 
1047
                            buf.write(previnbuf, 0, prevSize - 1);
 
1048
                        in.reset();
 
1049
                        skipFully(in, 1);
 
1050
                        if (prevSize >= 1) {    // is there a byte to save?
 
1051
                            // yes, save one from previous and one from current
 
1052
                            previnbuf[0] = previnbuf[prevSize - 1];
 
1053
                            previnbuf[1] = inbuf[0];
 
1054
                            prevSize = 2;
 
1055
                        } else {
 
1056
                            // no previous bytes to save, can only save current
 
1057
                            previnbuf[0] = inbuf[0];
 
1058
                            prevSize = 1;
 
1059
                        }
 
1060
                    } else {
 
1061
                        // first, write out data from previous buffer before
 
1062
                        // we dump it
 
1063
                        if (prevSize > 0 && sin == null)
 
1064
                            buf.write(previnbuf, 0, prevSize);
 
1065
                        // all the bytes we're skipping are saved in previnbuf
 
1066
                        prevSize = skip;
 
1067
                        in.reset();
 
1068
                        skipFully(in, prevSize);
 
1069
                        // swap buffers
 
1070
                        byte[] tmp = inbuf;
 
1071
                        inbuf = previnbuf;
 
1072
                        previnbuf = tmp;
 
1073
                    }
 
1074
                    first = false;
 
1075
                }
 
1076
 
 
1077
                /*
 
1078
                 * Create a MimeBody element to represent this body part.
 
1079
                 */
 
1080
                MimeBodyPart part;
 
1081
                if (sin != null) {
 
1082
                    part = createMimeBodyPart(sin.newStream(start, end));
 
1083
                } else {
 
1084
                    // write out data from previous buffer, not including EOL
 
1085
                    if (prevSize - eolLen > 0)
 
1086
                        buf.write(previnbuf, 0, prevSize - eolLen);
 
1087
                    // if we didn't find a trailing boundary,
 
1088
                    // the current buffer has data we need too
 
1089
                    if (!complete && inSize > 0)
 
1090
                        buf.write(inbuf, 0, inSize);
 
1091
                    part = createMimeBodyPart(headers, buf.toByteArray());
 
1092
                }
 
1093
                super.addBodyPart(part);
 
1094
            }
 
1095
        } catch (IOException ioex) {
 
1096
            throw new MessagingException("IO Error", ioex);
 
1097
        } finally {
 
1098
            try {
 
1099
                in.close();
 
1100
            } catch (IOException cex) {
 
1101
                // ignore
 
1102
            }
 
1103
        }
 
1104
 
 
1105
        parsed = true;
 
1106
    }
 
1107
 
 
1108
    /**
 
1109
     * Read data from the input stream to fill the buffer starting
 
1110
     * at the specified offset with the specified number of bytes.
 
1111
     * If len is zero, return zero.  If at EOF, return -1.  Otherwise,
 
1112
     * return the number of bytes read.  Call the read method on the
 
1113
     * input stream as many times as necessary to read len bytes.
 
1114
     *
 
1115
     * @param   in      InputStream to read from
 
1116
     * @param   buf     buffer to read into
 
1117
     * @param   off     offset in the buffer for first byte
 
1118
     * @param   len     number of bytes to read
 
1119
     * @return          -1 on EOF, otherwise number of bytes read
 
1120
     * @exception       IOException     on I/O errors
 
1121
     */
 
1122
    private static int readFully(InputStream in, byte[] buf, int off, int len)
 
1123
                                throws IOException {
 
1124
        if (len == 0)
 
1125
            return 0;
 
1126
        int total = 0;
 
1127
        while (len > 0) {
 
1128
            int bsize = in.read(buf, off, len);
 
1129
            if (bsize <= 0)     // should never be zero
 
1130
                break;
 
1131
            off += bsize;
 
1132
            total += bsize;
 
1133
            len -= bsize;
 
1134
        }
 
1135
        return total > 0 ? total : -1;
 
1136
    }
 
1137
 
 
1138
    /**
 
1139
     * Skip the specified number of bytes, repeatedly calling
 
1140
     * the skip method as necessary.
 
1141
     */
 
1142
    private void skipFully(InputStream in, long offset) throws IOException {
 
1143
        while (offset > 0) {
 
1144
            long cur = in.skip(offset);
 
1145
            if (cur <= 0)
 
1146
                throw new EOFException("can't skip");
 
1147
            offset -= cur;
 
1148
        }
 
1149
    }
 
1150
 
 
1151
    /**
 
1152
     * Create and return an InternetHeaders object that loads the
 
1153
     * headers from the given InputStream.  Subclasses can override
 
1154
     * this method to return a subclass of InternetHeaders, if
 
1155
     * necessary.  This implementation simply constructs and returns
 
1156
     * an InternetHeaders object.
 
1157
     *
 
1158
     * @param   is      the InputStream to read the headers from
 
1159
     * @exception       MessagingException
 
1160
     * @since           JavaMail 1.2
 
1161
     */
 
1162
    protected InternetHeaders createInternetHeaders(InputStream is)
 
1163
                                throws MessagingException {
 
1164
        return new InternetHeaders(is);
 
1165
    }
 
1166
 
 
1167
    /**
 
1168
     * Create and return a MimeBodyPart object to represent a
 
1169
     * body part parsed from the InputStream.  Subclasses can override
 
1170
     * this method to return a subclass of MimeBodyPart, if
 
1171
     * necessary.  This implementation simply constructs and returns
 
1172
     * a MimeBodyPart object.
 
1173
     *
 
1174
     * @param   headers         the headers for the body part
 
1175
     * @param   content         the content of the body part
 
1176
     * @exception               MessagingException
 
1177
     * @since                   JavaMail 1.2
 
1178
     */
 
1179
    protected MimeBodyPart createMimeBodyPart(InternetHeaders headers,
 
1180
                                byte[] content) throws MessagingException {
 
1181
        return new MimeBodyPart(headers, content);
 
1182
    }
 
1183
 
 
1184
    /**
 
1185
     * Create and return a MimeBodyPart object to represent a
 
1186
     * body part parsed from the InputStream.  Subclasses can override
 
1187
     * this method to return a subclass of MimeBodyPart, if
 
1188
     * necessary.  This implementation simply constructs and returns
 
1189
     * a MimeBodyPart object.
 
1190
     *
 
1191
     * @param   is              InputStream containing the body part
 
1192
     * @exception               MessagingException
 
1193
     * @since                   JavaMail 1.2
 
1194
     */
 
1195
    protected MimeBodyPart createMimeBodyPart(InputStream is)
 
1196
                                throws MessagingException {
 
1197
        return new MimeBodyPart(is);
 
1198
    }
 
1199
}