]> git.argeo.org Git - gpl/argeo-slc.git/blob - ext/javax.mail.mbox/src/com/sun/mail/mbox/MessageLoader.java
Make logging synchronous during native image build
[gpl/argeo-slc.git] / ext / javax.mail.mbox / src / com / sun / mail / mbox / MessageLoader.java
1 /*
2 * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
3 *
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.
7 *
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.
13 *
14 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15 */
16
17 package com.sun.mail.mbox;
18
19 import java.io.*;
20 import java.util.*;
21
22 /**
23 * A support class that contains the state and logic needed when
24 * loading messages from a folder.
25 */
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();
37 private char[] line;
38
39 public MessageLoader(TempFile temp) {
40 this.temp = temp;
41 }
42
43 /**
44 * Load messages from the given file descriptor, starting at the
45 * specified offset, adding the MessageMetadata to the list. <p>
46 *
47 * The data is assumed to be in UNIX mbox format, with newlines
48 * only as the line terminators.
49 */
50 public int load(FileDescriptor fd, long offset,
51 List<MboxFolder.MessageMetadata> msgs)
52 throws IOException {
53 // XXX - could allocate and deallocate buffers here
54 int loaded = 0;
55 try {
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();
60 pos = len = 0;
61 line = new char[LINELEN];
62 buf = new byte[64 * 1024];
63 fos = temp.getAppendStream();
64 int n;
65 // keep loading messages as long as we have headers
66 while ((n = skipHeader(loaded == 0)) >= 0) {
67 long start;
68 if (n == 0) {
69 // didn't find a Content-Length, skip the body
70 start = skipBody();
71 if (start < 0) {
72 // md.end = -1;
73 md.dataend = -1;
74 msgs.add(md);
75 loaded++;
76 break;
77 }
78 md.dataend = start;
79 } else {
80 // skip over the body
81 skip(n);
82 md.dataend = off;
83 int b;
84 // skip any blank lines after the body
85 while ((b = get()) >= 0) {
86 if (b != '\n')
87 break;
88 }
89 start = off;
90 if (b >= 0)
91 start--; // back up one byte if not at EOF
92 }
93 // md.end = start;
94 prevend = start;
95 msgs.add(md);
96 loaded++;
97 }
98 } finally {
99 try {
100 fis.close();
101 } catch (IOException ex) {
102 // ignore
103 }
104 try {
105 fos.close();
106 } catch (IOException ex) {
107 // ignore
108 }
109 line = null;
110 buf = null;
111 }
112 return loaded;
113 }
114
115 /**
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.
119 * return -1 on EOF.
120 */
121 private int skipHeader(boolean first) throws IOException {
122 int clen = 0;
123 boolean bol = true;
124 int lpos = -1;
125 int b;
126 boolean saw_unix_from = false;
127 int lineno = 0;
128 md = new MboxFolder.MessageMetadata();
129 md.start = prevend;
130 md.recent = true;
131 while ((b = get()) >= 0) {
132 if (bol) {
133 if (b == '\n')
134 break;
135 lpos = 0;
136 }
137 if (b == '\n') {
138 bol = true;
139 lineno++;
140 // newline at end of line, was the line one of the headers
141 // we're looking for?
142 if (lpos > 7) {
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();
151 try {
152 clen = Integer.parseInt(s);
153 } catch (NumberFormatException ex) {
154 // ignore it
155 }
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)
160 md.recent = false;
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)
165 md.deleted = true;
166 // fast check for X-Dt-Delete-Time header
167 } else if (line[4] == '-' &&
168 isPrefix(s, "X-Dt-Delete-Time:")) {
169 md.deleted = true;
170 // fast check for X-IMAP header
171 } else if (line[5] == 'P' && s.startsWith("X-IMAP:")) {
172 md.imap = true;
173 }
174 }
175 } else {
176 // accumlate data in line buffer
177 bol = false;
178 if (lpos < 0) // ignoring this line
179 continue;
180 if (lpos == 0 && (b == ' ' || b == '\t'))
181 lpos = -1; // ignore continuation lines
182 else if (lpos < line.length)
183 line[lpos++] = (char)b;
184 }
185 }
186
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))
192 return -1;
193 else
194 return clen;
195 }
196
197 /**
198 * Does "s" start with "pre", ignoring case?
199 */
200 private static final boolean isPrefix(String s, String pre) {
201 return s.regionMatches(true, 0, pre, 0, pre.length());
202 }
203
204 /**
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.
208 */
209 private long skipBody() throws IOException {
210 boolean bol = true;
211 int lpos = -1;
212 long loff = off;
213 int b;
214 while ((b = get()) >= 0) {
215 if (bol) {
216 lpos = 0;
217 loff = off - 1;
218 }
219 if (b == '\n') {
220 bol = true;
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] == ' ')
224 return loff;
225 }
226 } else {
227 bol = false;
228 if (lpos < 0)
229 continue;
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;
234 }
235 }
236 return -1;
237 }
238
239 /**
240 * Skip "n" bytes, returning how much we were able to skip.
241 */
242 private final int skip(int n) throws IOException {
243 int n0 = n;
244 if (pos + n < len) {
245 pos += n; // can do it all within this buffer
246 off += n;
247 } else {
248 do {
249 n -= (len - pos); // skip rest of this buffer
250 off += (len - pos);
251 fill();
252 if (len <= 0) // ran out of data
253 return n0 - n;
254 } while (n > len);
255 pos += n;
256 off += n;
257 }
258 return n0;
259 }
260
261 /**
262 * Return the next byte.
263 */
264 private final int get() throws IOException {
265 if (pos >= len)
266 fill();
267 if (pos >= len)
268 return -1;
269 else {
270 off++;
271 return buf[pos++] & 0xff;
272 }
273 }
274
275 /**
276 * Fill our buffer with more data.
277 * Every buffer we read is also written to the temp file.
278 */
279 private final void fill() throws IOException {
280 len = fis.read(buf);
281 pos = 0;
282 if (len > 0)
283 fos.write(buf, 0, len);
284 }
285 }