]>
git.argeo.org Git - gpl/argeo-slc.git/blob - ext/javax.mail.mbox/src/com/sun/mail/mbox/MessageLoader.java
2 * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v. 2.0, which is available at
6 * http://www.eclipse.org/legal/epl-2.0.
8 * This Source Code may also be made available under the following Secondary
9 * Licenses when the conditions for such availability set forth in the
10 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11 * version 2 with the GNU Classpath Exception, which is available at
12 * https://www.gnu.org/software/classpath/license.html.
14 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
17 package com
.sun
.mail
.mbox
;
23 * A support class that contains the state and logic needed when
24 * loading messages from a folder.
26 final class MessageLoader
{
27 private final TempFile temp
;
28 private FileInputStream fis
= null;
29 private OutputStream fos
= null;
30 private int pos
, len
; // position in and length of buffer
31 private long off
; // current offset in temp file
32 private long prevend
; // the end of the previous message in temp file
33 private MboxFolder
.MessageMetadata md
;
34 private byte[] buf
= null;
35 // the length of the longest header we'll need to look at
36 private static final int LINELEN
= "Content-Length: XXXXXXXXXX".length();
39 public MessageLoader(TempFile temp
) {
44 * Load messages from the given file descriptor, starting at the
45 * specified offset, adding the MessageMetadata to the list. <p>
47 * The data is assumed to be in UNIX mbox format, with newlines
48 * only as the line terminators.
50 public int load(FileDescriptor fd
, long offset
,
51 List
<MboxFolder
.MessageMetadata
> msgs
)
53 // XXX - could allocate and deallocate buffers here
56 fis
= new FileInputStream(fd
);
57 if (fis
.skip(offset
) != offset
)
58 throw new EOFException("Failed to skip to offset " + offset
);
59 this.off
= prevend
= temp
.length();
61 line
= new char[LINELEN
];
62 buf
= new byte[64 * 1024];
63 fos
= temp
.getAppendStream();
65 // keep loading messages as long as we have headers
66 while ((n
= skipHeader(loaded
== 0)) >= 0) {
69 // didn't find a Content-Length, skip the body
84 // skip any blank lines after the body
85 while ((b
= get()) >= 0) {
91 start
--; // back up one byte if not at EOF
101 } catch (IOException ex
) {
106 } catch (IOException ex
) {
116 * Skip over the message header, returning the content length
117 * of the body, or 0 if no Content-Length header was seen.
118 * Update the MessageMetadata based on the headers seen.
121 private int skipHeader(boolean first
) throws IOException
{
126 boolean saw_unix_from
= false;
128 md
= new MboxFolder
.MessageMetadata();
131 while ((b
= get()) >= 0) {
140 // newline at end of line, was the line one of the headers
141 // we're looking for?
143 // XXX - make this more efficient?
144 String s
= new String(line
, 0, lpos
);
145 // fast check for Content-Length header
146 if (lineno
== 1 && line
[0] == 'F' && isPrefix(s
, "From ")) {
147 saw_unix_from
= true;
148 } else if (line
[7] == '-' &&
149 isPrefix(s
, "Content-Length:")) {
150 s
= s
.substring(15).trim();
152 clen
= Integer
.parseInt(s
);
153 } catch (NumberFormatException ex
) {
156 // fast check for Status header
157 } else if ((line
[1] == 't' || line
[1] == 'T') &&
158 isPrefix(s
, "Status:")) {
159 if (s
.indexOf('O') >= 0)
161 // fast check for X-Status header
162 } else if ((line
[3] == 't' || line
[3] == 'T') &&
163 isPrefix(s
, "X-Status:")) {
164 if (s
.indexOf('D') >= 0)
166 // fast check for X-Dt-Delete-Time header
167 } else if (line
[4] == '-' &&
168 isPrefix(s
, "X-Dt-Delete-Time:")) {
170 // fast check for X-IMAP header
171 } else if (line
[5] == 'P' && s
.startsWith("X-IMAP:")) {
176 // accumlate data in line buffer
178 if (lpos
< 0) // ignoring this line
180 if (lpos
== 0 && (b
== ' ' || b
== '\t'))
181 lpos
= -1; // ignore continuation lines
182 else if (lpos
< line
.length
)
183 line
[lpos
++] = (char)b
;
187 // if we hit EOF, or this is the first message we're loading and
188 // it doesn't have a UNIX From line, return EOF.
189 // (After the first message, UNIX From lines are seen by skipBody
190 // to terminate the message.)
191 if (b
< 0 || (first
&& !saw_unix_from
))
198 * Does "s" start with "pre", ignoring case?
200 private static final boolean isPrefix(String s
, String pre
) {
201 return s
.regionMatches(true, 0, pre
, 0, pre
.length());
205 * Skip over the body of the message looking for a line that starts
206 * with "From ". If found, return the offset of the beginning of
207 * that line. Return -1 on EOF.
209 private long skipBody() throws IOException
{
214 while ((b
= get()) >= 0) {
221 if (lpos
>= 5) { // have enough data to test?
222 if (line
[0] == 'F' && line
[1] == 'r' && line
[2] == 'o' &&
223 line
[3] == 'm' && line
[4] == ' ')
230 if (lpos
== 0 && b
!= 'F')
231 lpos
= -1; // ignore lines that don't start with F
232 else if (lpos
< 5) // only need first 5 chars to test
233 line
[lpos
++] = (char)b
;
240 * Skip "n" bytes, returning how much we were able to skip.
242 private final int skip(int n
) throws IOException
{
245 pos
+= n
; // can do it all within this buffer
249 n
-= (len
- pos
); // skip rest of this buffer
252 if (len
<= 0) // ran out of data
262 * Return the next byte.
264 private final int get() throws IOException
{
271 return buf
[pos
++] & 0xff;
276 * Fill our buffer with more data.
277 * Every buffer we read is also written to the temp file.
279 private final void fill() throws IOException
{
283 fos
.write(buf
, 0, len
);