11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 /*
27 * @(#)MimeMultipart.java 1.31 03/01/29
28 */
29
30
31
32 package com.sun.xml.internal.messaging.saaj.packaging.mime.internet;
33
34 import java.io.*;
35 import java.util.BitSet;
36
37 import javax.activation.DataSource;
38
39 import com.sun.xml.internal.messaging.saaj.packaging.mime.*;
40 import com.sun.xml.internal.messaging.saaj.packaging.mime.util.*;
41
42 import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
43
44 /**
45 * The MimeMultipart class is an implementation of the abstract Multipart
46 * class that uses MIME conventions for the multipart data. <p>
47 *
48 * A MimeMultipart is obtained from a MimePart whose primary type
49 * is "multipart" (by invoking the part's <code>getContent()</code> method)
50 * or it can be created by a client as part of creating a new MimeMessage. <p>
51 *
52 * The default multipart subtype is "mixed". The other multipart
53 * subtypes, such as "alternative", "related", and so on, can be
54 * implemented as subclasses of MimeMultipart with additional methods
55 * to implement the additional semantics of that type of multipart
56 * content. The intent is that service providers, mail JavaBean writers
57 * and mail clients will write many such subclasses and their Command
58 * Beans, and will install them into the JavaBeans Activation
59 * Framework, so that any JavaMail implementation and its clients can
60 * transparently find and use these classes. Thus, a MIME multipart
61 * handler is treated just like any other type handler, thereby
62 * decoupling the process of providing multipart handlers from the
63 * JavaMail API. Lacking these additional MimeMultipart subclasses,
64 * all subtypes of MIME multipart data appear as MimeMultipart objects. <p>
65 *
66 * An application can directly construct a MIME multipart object of any
67 * subtype by using the <code>MimeMultipart(String subtype)</code>
68 * constructor. For example, to create a "multipart/alternative" object,
69 * use <code>new MimeMultipart("alternative")</code>.
70 *
71 */
72
73 //TODO: cleanup the SharedInputStream handling
74 public class BMMimeMultipart extends MimeMultipart {
75
76 /*
77 * When true it indicates parsing hasnt been done at all
78 */
79 private boolean begining = true;
80
81 int[] bcs = new int[256];
82 int[] gss = null;
83 private static final int BUFFER_SIZE = 4096;
84 private byte[] buffer = new byte[BUFFER_SIZE];
85 private byte[] prevBuffer = new byte[BUFFER_SIZE];
86 private BitSet lastPartFound = new BitSet(1);
87
88 // cached inputstream which is possibly partially consumed
89 private InputStream in = null;
90 private String boundary = null;
127 contentType.setParameter("boundary", boundary);
128 */
129 }
130
131 /**
132 * Constructs a MimeMultipart object and its bodyparts from the
133 * given DataSource. <p>
134 *
135 * This constructor handles as a special case the situation where the
136 * given DataSource is a MultipartDataSource object. In this case, this
137 * method just invokes the superclass (i.e., Multipart) constructor
138 * that takes a MultipartDataSource object. <p>
139 *
140 * Otherwise, the DataSource is assumed to provide a MIME multipart
141 * byte stream. The <code>parsed</code> flag is set to false. When
142 * the data for the body parts are needed, the parser extracts the
143 * "boundary" parameter from the content type of this DataSource,
144 * skips the 'preamble' and reads bytes till the terminating
145 * boundary and creates MimeBodyParts for each part of the stream.
146 *
147 * @param ds DataSource, can be a MultipartDataSource.
148 * @param ct content type.
149 * @exception MessagingException in case of error.
150 */
151 public BMMimeMultipart(DataSource ds, ContentType ct)
152 throws MessagingException {
153 super(ds,ct);
154 boundary = ct.getParameter("boundary");
155 /*
156 if (ds instanceof MultipartDataSource) {
157 // ask super to do this for us.
158 setMultipartDataSource((MultipartDataSource)ds);
159 return;
160 }
161
162 // 'ds' was not a MultipartDataSource, we have
163 // to parse this ourself.
164 parsed = false;
165 this.ds = ds;
166 if (ct==null)
167 contentType = new ContentType(ds.getContentType());
168 else
169 contentType = ct;
170 */
171
172 }
173
193 }
194
195 /**
196 * Parse the InputStream from our DataSource, constructing the
197 * appropriate MimeBodyParts. The <code>parsed</code> flag is
198 * set to true, and if true on entry nothing is done. This
199 * method is called by all other methods that need data for
200 * the body parts, to make sure the data has been parsed.
201 *
202 * @since JavaMail 1.2
203 */
204 @Override
205 protected void parse() throws MessagingException {
206 if (parsed)
207 return;
208
209 initStream();
210
211 SharedInputStream sin = null;
212 if (in instanceof SharedInputStream) {
213 sin = (SharedInputStream)in;
214 }
215
216 String bnd = "--" + boundary;
217 byte[] bndbytes = ASCIIUtility.getBytes(bnd);
218 try {
219 parse(in, bndbytes, sin);
220 } catch (IOException ioex) {
221 throw new MessagingException("IO Error", ioex);
222 } catch (Exception ex) {
223 throw new MessagingException("Error", ex);
224 }
225
226 parsed = true;
227 }
228
229 public boolean lastBodyPartFound() {
230 return lastPartFound.get(0);
231 }
232
233 public MimeBodyPart getNextPart(
259 "End of Stream encountered while reading part headers");
260 }
261 long[] v = new long[1];
262 v[0] = -1; // just to ensure the code later sets it correctly
263 b = readBody(stream, pattern, v, null, sin);
264 // looks like this check has to be disabled
265 // it is allowed to have Mime Package without closing boundary
266 if (!ignoreMissingEndBoundary) {
267 if ((b == -1) && !lastBodyPartFound()) {
268 throw new MessagingException("Missing End Boundary for Mime Package : EOF while skipping headers");
269 }
270 }
271 long end = v[0];
272 MimeBodyPart mbp = createMimeBodyPart(sin.newStream(start, end));
273 addBodyPart(mbp);
274 return mbp;
275
276 } else {
277 InternetHeaders headers = createInternetHeaders(stream);
278 ByteOutputStream baos = new ByteOutputStream();
279 b = readBody(stream, pattern, null,baos, null);
280 // looks like this check has to be disabled
281 // in the old impl it is allowed to have Mime Package
282 // without closing boundary
283 if (!ignoreMissingEndBoundary) {
284 if ((b == -1) && !lastBodyPartFound()) {
285 throw new MessagingException("Missing End Boundary for Mime Package : EOF while skipping headers");
286 }
287 }
288 MimeBodyPart mbp = createMimeBodyPart(
289 headers, baos.getBytes(), baos.getCount());
290 addBodyPart(mbp);
291 return mbp;
292 }
293
294 }
295
296 public boolean parse(
297 InputStream stream, byte[] pattern, SharedInputStream sin)
298 throws Exception {
299
300 while (!lastPartFound.get(0) && (b != -1)) {
301 getNextPart(stream, pattern, sin);
302 }
303 return true;
304 }
305
306 private int readHeaders(InputStream is) throws Exception {
307 // if the headers are to end properly then there has to be CRLF
308 // actually we just need to mark the start and end positions
309 int b = is.read();
310 while(b != -1) {
311 // when it is a shared input stream no need to copy
312 if (b == '\r') {
313 b = is.read();
314 if (b == '\n') {
315 b = is.read();
316 if (b == '\r') {
317 b = is.read();
318 if (b == '\n') {
319 return b;
320 } else {
321 continue;
322 }
323 } else {
324 continue;
325 }
326 } else {
327 continue;
328 }
329 }
330 b = is.read();
368 int bufferLength = is.read(buffer, 0, patternLength);
369 if (bufferLength == -1) {
370 eof.flip(0);
371 } else if (bufferLength < patternLength) {
372 //repeatedly read patternLength - bufferLength
373 int temp = 0;
374 long pos = 0;
375 int i = bufferLength;
376 for (; i < patternLength; i++) {
377 if (sin != null) {
378 pos = sin.getPosition();
379 }
380 temp = is.read();
381 if (temp == -1) {
382 eof.flip(0);
383 if (sin != null) {
384 posVector[0] = pos;
385 }
386 break;
387 }
388 buffer[i] = (byte)temp;
389 }
390 bufferLength=i;
391 }
392 return bufferLength;
393 }
394
395 public boolean find(InputStream is, byte[] pattern, SharedInputStream sin)
396 throws Exception {
397 int i;
398 int l = pattern.length;
399 int lx = l -1;
400 BitSet eof = new BitSet(1);
401 long[] posVector = new long[1];
402
403 while (true) {
404 is.mark(l);
405 readNext(is, buffer, l, eof, posVector, sin);
406 if (eof.get(0)) {
407 // End of stream
408 return false;
409 }
410
411 /*
412 if (bufferLength < l) {
413 //is.reset();
414 return false;
415 }*/
416
417 for(i = lx; i >= 0; i--) {
418 if (buffer[i] != pattern[i]) {
419 break;
420 }
421 }
422
423 if (i < 0) {
424 // found the boundary, skip *LWSP-char and CRLF
425 if (!skipLWSPAndCRLF(is)) {
426 throw new Exception("Boundary does not terminate with CRLF");
427 }
428 return true;
429 }
430
431 int s = Math.max(i + 1 - bcs[buffer[i] & 0x7f], gss[i]);
432 is.reset();
433 is.skip(s);
434 }
435 }
436
437 public boolean find(
438 InputStream is, byte[] pattern, long[] posVector,
439 ByteOutputStream out, SharedInputStream sin) throws Exception {
440 int i;
441 int l = pattern.length;
442 int lx = l -1;
443 int bufferLength = 0;
444 int s = 0;
445 long endPos = -1;
446 byte[] tmp = null;
447
448 boolean first = true;
449 BitSet eof = new BitSet(1);
450
451 while (true) {
452 is.mark(l);
453 if (!first) {
454 tmp = prevBuffer;
455 prevBuffer = buffer;
456 buffer = tmp;
457 }
458 if (sin != null) {
459 endPos = sin.getPosition();
460 }
461
462 bufferLength = readNext(is, buffer, l, eof, posVector, sin);
474 }
475 return true;
476 }
477
478 if (bufferLength < l) {
479 if (sin != null) {
480 //endPos = sin.getPosition();
481 //posVector[0] = endPos;
482 } else {
483 // looks like it is allowed to not have a closing boundary
484 // in the old implementation
485 out.write(buffer, 0, bufferLength);
486 }
487 // looks like it is allowed to not have a closing boundary
488 // in the old implementation
489 //return false;
490 b = -1;
491 return true;
492 }
493
494 for(i = lx; i >= 0; i--) {
495 if (buffer[i] != pattern[i]) {
496 break;
497 }
498 }
499
500 if (i < 0) {
501 if (s > 0) {
502 //looks like the earlier impl allowed just an LF
503 // so if s == 1 : it must be an LF
504 // if s == 2 : it must be a CR LF
505 if (s <= 2) {
506 //it could be "some-char\n" so write some-char
507 if (s == 2) {
508 if (prevBuffer[1] == '\n') {
509 if (prevBuffer[0] != '\r' && prevBuffer[0] != '\n') {
510 out.write(prevBuffer,0,1);
511 }
512 if (sin != null) {
513 posVector[0] = endPos;
514 }
515
516 } else {
517 throw new Exception(
518 "Boundary characters encountered in part Body " +
519 "without a preceeding CRLF");
520 }
521
522 } else if (s==1) {
523 if (prevBuffer[0] != '\n') {
524 throw new Exception(
525 "Boundary characters encountered in part Body " +
526 "without a preceeding CRLF");
527 }else {
528 if (sin != null) {
529 posVector[0] = endPos;
530 }
531 }
532 }
533
534 } else if (s > 2) {
535 if ((prevBuffer[s-2] == '\r') && (prevBuffer[s-1] == '\n')) {
536 if (sin != null) {
537 posVector[0] = endPos - 2;
538 } else {
539 out.write(prevBuffer, 0, s - 2);
540 }
541 } else if (prevBuffer[s-1] == '\n') {
542 //old impl allowed just a \n
543 if (sin != null) {
544 posVector[0] = endPos - 1;
545 } else {
546 out.write(prevBuffer, 0, s - 1);
547 }
548 } else {
549 throw new Exception(
550 "Boundary characters encountered in part Body " +
551 "without a preceeding CRLF");
552 }
553 }
554 }
555 // found the boundary, skip *LWSP-char and CRLF
556 if (!skipLWSPAndCRLF(is)) {
557 //throw new Exception(
558 // "Boundary does not terminate with CRLF");
559 }
560 return true;
561 }
562
563 if ((s > 0) && (sin == null)) {
564 if (prevBuffer[s-1] == (byte)13) {
565 // if buffer[0] == (byte)10
566 if (buffer[0] == (byte)10) {
567 int j;
568 for(j = lx-1; j > 0; j--) {
569 if (buffer[j+1] != pattern[j]) {
570 break;
571 }
572 }
573 if (j == 0) {
574 // matched the pattern excluding the last char of the pattern
575 // so dont write the CR into stream
576 out.write(prevBuffer,0,s-1);
577 } else {
578 out.write(prevBuffer,0,s);
579 }
580 } else {
581 out.write(prevBuffer, 0, s);
582 }
583 } else {
584 out.write(prevBuffer, 0, s);
585 }
586 }
587
588 s = Math.max(i + 1 - bcs[buffer[i] & 0x7f], gss[i]);
589 is.reset();
590 is.skip(s);
591 if (first) {
592 first = false;
593 }
594 }
595 }
596
597 private boolean skipLWSPAndCRLF(InputStream is) throws Exception {
598
656 }
657 return false;
658 }
659
660 private void compile(byte[] pattern) {
661 int l = pattern.length;
662
663 int i;
664 int j;
665
666 // Copied from J2SE 1.4 regex code
667 // java.util.regex.Pattern.java
668
669 // Initialise Bad Character Shift table
670 for (i = 0; i < l; i++) {
671 bcs[pattern[i]] = i + 1;
672 }
673
674 // Initialise Good Suffix Shift table
675 gss = new int[l];
676 NEXT: for (i = l; i > 0; i--) {
677 // j is the beginning index of suffix being considered
678 for (j = l - 1; j >= i; j--) {
679 // Testing for good suffix
680 if (pattern[j] == pattern[j - i]) {
681 // pattern[j..len] is a good suffix
682 gss[j - 1] = i;
683 } else {
684 // No match. The array has already been
685 // filled up with correct values before.
686 continue NEXT;
687 }
688 }
689 while (j > 0) {
690 gss[--j] = i;
691 }
692 }
693 gss[l - 1] = 1;
694 }
695
696
739 // put out last boundary
740 OutputUtil.writeAsAscii(bnd, os);
741 OutputUtil.writeAsAscii("--", os);
742 }
743 }
744
745 public void setInputStream(InputStream is) {
746 this.in = is;
747 }
748
749 public InputStream getInputStream() {
750 return this.in;
751 }
752
753 public void setBoundary(String bnd) {
754 this.boundary = bnd;
755 if (this.contentType != null) {
756 this.contentType.setParameter("boundary", bnd);
757 }
758 }
759 public String getBoundary() {
760 return this.boundary;
761 }
762
763 public boolean isEndOfStream() {
764 return (b == -1);
765 }
766
767 public void setLazyAttachments(boolean flag) {
768 lazyAttachments = flag;
769 }
770
771 }
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 /*
27 * @(#)MimeMultipart.java 1.31 03/01/29
28 */
29
30
31 package com.sun.xml.internal.messaging.saaj.packaging.mime.internet;
32
33 import com.sun.xml.internal.messaging.saaj.packaging.mime.MessagingException;
34 import com.sun.xml.internal.messaging.saaj.packaging.mime.util.ASCIIUtility;
35 import com.sun.xml.internal.messaging.saaj.packaging.mime.util.OutputUtil;
36 import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
37
38 import javax.activation.DataSource;
39 import java.io.BufferedInputStream;
40 import java.io.ByteArrayInputStream;
41 import java.io.IOException;
42 import java.io.InputStream;
43 import java.io.OutputStream;
44 import java.util.BitSet;
45
46 /**
47 * The MimeMultipart class is an implementation of the abstract Multipart
48 * class that uses MIME conventions for the multipart data. <p>
49 *
50 * A MimeMultipart is obtained from a MimePart whose primary type
51 * is "multipart" (by invoking the part's <code>getContent()</code> method)
52 * or it can be created by a client as part of creating a new MimeMessage. <p>
53 *
54 * The default multipart subtype is "mixed". The other multipart
55 * subtypes, such as "alternative", "related", and so on, can be
56 * implemented as subclasses of MimeMultipart with additional methods
57 * to implement the additional semantics of that type of multipart
58 * content. The intent is that service providers, mail JavaBean writers
59 * and mail clients will write many such subclasses and their Command
60 * Beans, and will install them into the JavaBeans Activation
61 * Framework, so that any JavaMail implementation and its clients can
62 * transparently find and use these classes. Thus, a MIME multipart
63 * handler is treated just like any other type handler, thereby
64 * decoupling the process of providing multipart handlers from the
65 * JavaMail API. Lacking these additional MimeMultipart subclasses,
66 * all subtypes of MIME multipart data appear as MimeMultipart objects. <p>
67 *
68 * An application can directly construct a MIME multipart object of any
69 * subtype by using the <code>MimeMultipart(String subtype)</code>
70 * constructor. For example, to create a "multipart/alternative" object,
71 * use <code>new MimeMultipart("alternative")</code>.
72 */
73
74 //TODO: cleanup the SharedInputStream handling
75 public class BMMimeMultipart extends MimeMultipart {
76
77 /*
78 * When true it indicates parsing hasnt been done at all
79 */
80 private boolean begining = true;
81
82 int[] bcs = new int[256];
83 int[] gss = null;
84 private static final int BUFFER_SIZE = 4096;
85 private byte[] buffer = new byte[BUFFER_SIZE];
86 private byte[] prevBuffer = new byte[BUFFER_SIZE];
87 private BitSet lastPartFound = new BitSet(1);
88
89 // cached inputstream which is possibly partially consumed
90 private InputStream in = null;
91 private String boundary = null;
128 contentType.setParameter("boundary", boundary);
129 */
130 }
131
132 /**
133 * Constructs a MimeMultipart object and its bodyparts from the
134 * given DataSource. <p>
135 *
136 * This constructor handles as a special case the situation where the
137 * given DataSource is a MultipartDataSource object. In this case, this
138 * method just invokes the superclass (i.e., Multipart) constructor
139 * that takes a MultipartDataSource object. <p>
140 *
141 * Otherwise, the DataSource is assumed to provide a MIME multipart
142 * byte stream. The <code>parsed</code> flag is set to false. When
143 * the data for the body parts are needed, the parser extracts the
144 * "boundary" parameter from the content type of this DataSource,
145 * skips the 'preamble' and reads bytes till the terminating
146 * boundary and creates MimeBodyParts for each part of the stream.
147 *
148 * @param ct content type.
149 * @param ds DataSource, can be a MultipartDataSource.
150 * @throws MessagingException in case of error.
151 */
152 public BMMimeMultipart(DataSource ds, ContentType ct)
153 throws MessagingException {
154 super(ds, ct);
155 boundary = ct.getParameter("boundary");
156 /*
157 if (ds instanceof MultipartDataSource) {
158 // ask super to do this for us.
159 setMultipartDataSource((MultipartDataSource)ds);
160 return;
161 }
162
163 // 'ds' was not a MultipartDataSource, we have
164 // to parse this ourself.
165 parsed = false;
166 this.ds = ds;
167 if (ct==null)
168 contentType = new ContentType(ds.getContentType());
169 else
170 contentType = ct;
171 */
172
173 }
174
194 }
195
196 /**
197 * Parse the InputStream from our DataSource, constructing the
198 * appropriate MimeBodyParts. The <code>parsed</code> flag is
199 * set to true, and if true on entry nothing is done. This
200 * method is called by all other methods that need data for
201 * the body parts, to make sure the data has been parsed.
202 *
203 * @since JavaMail 1.2
204 */
205 @Override
206 protected void parse() throws MessagingException {
207 if (parsed)
208 return;
209
210 initStream();
211
212 SharedInputStream sin = null;
213 if (in instanceof SharedInputStream) {
214 sin = (SharedInputStream) in;
215 }
216
217 String bnd = "--" + boundary;
218 byte[] bndbytes = ASCIIUtility.getBytes(bnd);
219 try {
220 parse(in, bndbytes, sin);
221 } catch (IOException ioex) {
222 throw new MessagingException("IO Error", ioex);
223 } catch (Exception ex) {
224 throw new MessagingException("Error", ex);
225 }
226
227 parsed = true;
228 }
229
230 public boolean lastBodyPartFound() {
231 return lastPartFound.get(0);
232 }
233
234 public MimeBodyPart getNextPart(
260 "End of Stream encountered while reading part headers");
261 }
262 long[] v = new long[1];
263 v[0] = -1; // just to ensure the code later sets it correctly
264 b = readBody(stream, pattern, v, null, sin);
265 // looks like this check has to be disabled
266 // it is allowed to have Mime Package without closing boundary
267 if (!ignoreMissingEndBoundary) {
268 if ((b == -1) && !lastBodyPartFound()) {
269 throw new MessagingException("Missing End Boundary for Mime Package : EOF while skipping headers");
270 }
271 }
272 long end = v[0];
273 MimeBodyPart mbp = createMimeBodyPart(sin.newStream(start, end));
274 addBodyPart(mbp);
275 return mbp;
276
277 } else {
278 InternetHeaders headers = createInternetHeaders(stream);
279 ByteOutputStream baos = new ByteOutputStream();
280 b = readBody(stream, pattern, null, baos, null);
281 // looks like this check has to be disabled
282 // in the old impl it is allowed to have Mime Package
283 // without closing boundary
284 if (!ignoreMissingEndBoundary) {
285 if ((b == -1) && !lastBodyPartFound()) {
286 throw new MessagingException("Missing End Boundary for Mime Package : EOF while skipping headers");
287 }
288 }
289 MimeBodyPart mbp = createMimeBodyPart(
290 headers, baos.getBytes(), baos.getCount());
291 addBodyPart(mbp);
292 return mbp;
293 }
294
295 }
296
297 public boolean parse(
298 InputStream stream, byte[] pattern, SharedInputStream sin)
299 throws Exception {
300
301 while (!lastPartFound.get(0) && (b != -1)) {
302 getNextPart(stream, pattern, sin);
303 }
304 return true;
305 }
306
307 private int readHeaders(InputStream is) throws Exception {
308 // if the headers are to end properly then there has to be CRLF
309 // actually we just need to mark the start and end positions
310 int b = is.read();
311 while (b != -1) {
312 // when it is a shared input stream no need to copy
313 if (b == '\r') {
314 b = is.read();
315 if (b == '\n') {
316 b = is.read();
317 if (b == '\r') {
318 b = is.read();
319 if (b == '\n') {
320 return b;
321 } else {
322 continue;
323 }
324 } else {
325 continue;
326 }
327 } else {
328 continue;
329 }
330 }
331 b = is.read();
369 int bufferLength = is.read(buffer, 0, patternLength);
370 if (bufferLength == -1) {
371 eof.flip(0);
372 } else if (bufferLength < patternLength) {
373 //repeatedly read patternLength - bufferLength
374 int temp = 0;
375 long pos = 0;
376 int i = bufferLength;
377 for (; i < patternLength; i++) {
378 if (sin != null) {
379 pos = sin.getPosition();
380 }
381 temp = is.read();
382 if (temp == -1) {
383 eof.flip(0);
384 if (sin != null) {
385 posVector[0] = pos;
386 }
387 break;
388 }
389 buffer[i] = (byte) temp;
390 }
391 bufferLength = i;
392 }
393 return bufferLength;
394 }
395
396 public boolean find(InputStream is, byte[] pattern, SharedInputStream sin)
397 throws Exception {
398 int i;
399 int l = pattern.length;
400 int lx = l - 1;
401 BitSet eof = new BitSet(1);
402 long[] posVector = new long[1];
403
404 while (true) {
405 is.mark(l);
406 readNext(is, buffer, l, eof, posVector, sin);
407 if (eof.get(0)) {
408 // End of stream
409 return false;
410 }
411
412 /*
413 if (bufferLength < l) {
414 //is.reset();
415 return false;
416 }*/
417
418 for (i = lx; i >= 0; i--) {
419 if (buffer[i] != pattern[i]) {
420 break;
421 }
422 }
423
424 if (i < 0) {
425 // found the boundary, skip *LWSP-char and CRLF
426 if (!skipLWSPAndCRLF(is)) {
427 throw new Exception("Boundary does not terminate with CRLF");
428 }
429 return true;
430 }
431
432 int s = Math.max(i + 1 - bcs[buffer[i] & 0x7f], gss[i]);
433 is.reset();
434 is.skip(s);
435 }
436 }
437
438 public boolean find(
439 InputStream is, byte[] pattern, long[] posVector,
440 ByteOutputStream out, SharedInputStream sin) throws Exception {
441 int i;
442 int l = pattern.length;
443 int lx = l - 1;
444 int bufferLength = 0;
445 int s = 0;
446 long endPos = -1;
447 byte[] tmp = null;
448
449 boolean first = true;
450 BitSet eof = new BitSet(1);
451
452 while (true) {
453 is.mark(l);
454 if (!first) {
455 tmp = prevBuffer;
456 prevBuffer = buffer;
457 buffer = tmp;
458 }
459 if (sin != null) {
460 endPos = sin.getPosition();
461 }
462
463 bufferLength = readNext(is, buffer, l, eof, posVector, sin);
475 }
476 return true;
477 }
478
479 if (bufferLength < l) {
480 if (sin != null) {
481 //endPos = sin.getPosition();
482 //posVector[0] = endPos;
483 } else {
484 // looks like it is allowed to not have a closing boundary
485 // in the old implementation
486 out.write(buffer, 0, bufferLength);
487 }
488 // looks like it is allowed to not have a closing boundary
489 // in the old implementation
490 //return false;
491 b = -1;
492 return true;
493 }
494
495 for (i = lx; i >= 0; i--) {
496 if (buffer[i] != pattern[i]) {
497 break;
498 }
499 }
500
501 if (i < 0) {
502 if (s > 0) {
503 //looks like the earlier impl allowed just an LF
504 // so if s == 1 : it must be an LF
505 // if s == 2 : it must be a CR LF
506 if (s <= 2) {
507 //it could be "some-char\n" so write some-char
508 if (s == 2) {
509 if (prevBuffer[1] == '\n') {
510 if (prevBuffer[0] != '\r' && prevBuffer[0] != '\n') {
511 out.write(prevBuffer, 0, 1);
512 }
513 if (sin != null) {
514 posVector[0] = endPos;
515 }
516
517 } else {
518 throw new Exception(
519 "Boundary characters encountered in part Body " +
520 "without a preceeding CRLF");
521 }
522
523 } else if (s == 1) {
524 if (prevBuffer[0] != '\n') {
525 throw new Exception(
526 "Boundary characters encountered in part Body " +
527 "without a preceeding CRLF");
528 } else {
529 if (sin != null) {
530 posVector[0] = endPos;
531 }
532 }
533 }
534
535 } else if (s > 2) {
536 if ((prevBuffer[s - 2] == '\r') && (prevBuffer[s - 1] == '\n')) {
537 if (sin != null) {
538 posVector[0] = endPos - 2;
539 } else {
540 out.write(prevBuffer, 0, s - 2);
541 }
542 } else if (prevBuffer[s - 1] == '\n') {
543 //old impl allowed just a \n
544 if (sin != null) {
545 posVector[0] = endPos - 1;
546 } else {
547 out.write(prevBuffer, 0, s - 1);
548 }
549 } else {
550 throw new Exception(
551 "Boundary characters encountered in part Body " +
552 "without a preceeding CRLF");
553 }
554 }
555 }
556 // found the boundary, skip *LWSP-char and CRLF
557 if (!skipLWSPAndCRLF(is)) {
558 //throw new Exception(
559 // "Boundary does not terminate with CRLF");
560 }
561 return true;
562 }
563
564 if ((s > 0) && (sin == null)) {
565 if (prevBuffer[s - 1] == (byte) 13) {
566 // if buffer[0] == (byte)10
567 if (buffer[0] == (byte) 10) {
568 int j;
569 for (j = lx - 1; j > 0; j--) {
570 if (buffer[j + 1] != pattern[j]) {
571 break;
572 }
573 }
574 if (j == 0) {
575 // matched the pattern excluding the last char of the pattern
576 // so dont write the CR into stream
577 out.write(prevBuffer, 0, s - 1);
578 } else {
579 out.write(prevBuffer, 0, s);
580 }
581 } else {
582 out.write(prevBuffer, 0, s);
583 }
584 } else {
585 out.write(prevBuffer, 0, s);
586 }
587 }
588
589 s = Math.max(i + 1 - bcs[buffer[i] & 0x7f], gss[i]);
590 is.reset();
591 is.skip(s);
592 if (first) {
593 first = false;
594 }
595 }
596 }
597
598 private boolean skipLWSPAndCRLF(InputStream is) throws Exception {
599
657 }
658 return false;
659 }
660
661 private void compile(byte[] pattern) {
662 int l = pattern.length;
663
664 int i;
665 int j;
666
667 // Copied from J2SE 1.4 regex code
668 // java.util.regex.Pattern.java
669
670 // Initialise Bad Character Shift table
671 for (i = 0; i < l; i++) {
672 bcs[pattern[i]] = i + 1;
673 }
674
675 // Initialise Good Suffix Shift table
676 gss = new int[l];
677 NEXT:
678 for (i = l; i > 0; i--) {
679 // j is the beginning index of suffix being considered
680 for (j = l - 1; j >= i; j--) {
681 // Testing for good suffix
682 if (pattern[j] == pattern[j - i]) {
683 // pattern[j..len] is a good suffix
684 gss[j - 1] = i;
685 } else {
686 // No match. The array has already been
687 // filled up with correct values before.
688 continue NEXT;
689 }
690 }
691 while (j > 0) {
692 gss[--j] = i;
693 }
694 }
695 gss[l - 1] = 1;
696 }
697
698
741 // put out last boundary
742 OutputUtil.writeAsAscii(bnd, os);
743 OutputUtil.writeAsAscii("--", os);
744 }
745 }
746
747 public void setInputStream(InputStream is) {
748 this.in = is;
749 }
750
751 public InputStream getInputStream() {
752 return this.in;
753 }
754
755 public void setBoundary(String bnd) {
756 this.boundary = bnd;
757 if (this.contentType != null) {
758 this.contentType.setParameter("boundary", bnd);
759 }
760 }
761
762 public String getBoundary() {
763 return this.boundary;
764 }
765
766 public boolean isEndOfStream() {
767 return (b == -1);
768 }
769
770 public void setLazyAttachments(boolean flag) {
771 lazyAttachments = flag;
772 }
773
774 }
|