зеркало из https://github.com/mozilla/gecko-dev.git
287 строки
9.1 KiB
Java
287 строки
9.1 KiB
Java
/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License
|
|
* Version 1.0 (the "License"); you may not use this file except in
|
|
* compliance with the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS"
|
|
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
|
* the License for the specific language governing rights and limitations
|
|
* under the License.
|
|
*
|
|
* The Original Code is the Grendel mail/news client.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape Communications
|
|
* Corporation. Portions created by Netscape are Copyright (C) 1997
|
|
* Netscape Communications Corporation. All Rights Reserved.
|
|
*
|
|
* Created: Jamie Zawinski <jwz@netscape.com>, 2 Oct 1997.
|
|
*/
|
|
|
|
package grendel.storage;
|
|
|
|
import calypso.util.Assert;
|
|
import calypso.util.ByteBuf;
|
|
|
|
import javax.mail.internet.InternetHeaders;
|
|
import javax.mail.Header;
|
|
|
|
import java.io.RandomAccessFile;
|
|
import java.io.IOException;
|
|
import java.io.EOFException;
|
|
import java.util.Enumeration;
|
|
|
|
import grendel.util.Constants;
|
|
|
|
class ParseBerkeleyFolderAndExpunge extends ParseBerkeleyFolder {
|
|
|
|
private static final boolean DEBUG = true;
|
|
|
|
protected RandomAccessFile in = null;
|
|
protected RandomAccessFile out = null;
|
|
protected byte bytes[] = null;
|
|
protected ByteBuf bytebuf = null;
|
|
protected byte blank_mozilla_status[] = null;
|
|
protected byte[] linebreak = null;
|
|
Enumeration msgs = null;
|
|
|
|
synchronized void expungeDeletedMessages(BerkeleyFolder f,
|
|
RandomAccessFile in,
|
|
RandomAccessFile out,
|
|
Enumeration old_msgs)
|
|
throws IOException {
|
|
|
|
Assert.Assertion(old_msgs != null);
|
|
|
|
total_message_count = 0; // recordOne() updates these.
|
|
undeleted_message_count = 0;
|
|
unread_message_count = 0;
|
|
deleted_message_bytes = 0;
|
|
|
|
this.in = in;
|
|
this.out = out;
|
|
bytes = new byte[1024 * 4];
|
|
bytebuf = new ByteBuf();
|
|
blank_mozilla_status = ("X-Mozilla-Status: 0000" +
|
|
Constants.LINEBREAK).getBytes();
|
|
|
|
// Figure out what line terminator is used in this file by searching
|
|
// forward for the first "\n", "\r", or "\r\n".
|
|
{
|
|
long pos = in.getFilePointer();
|
|
int b;
|
|
do {
|
|
b = in.read();
|
|
} while (b != -1 && b != '\r' && b != '\n');
|
|
|
|
if (b == -1 || (b == '\r' && (in.read() == '\n')))
|
|
linebreak = "\r\n".getBytes();
|
|
else if (b == '\n')
|
|
linebreak = "\n".getBytes();
|
|
else
|
|
linebreak = "\r".getBytes();
|
|
in.seek(pos);
|
|
}
|
|
|
|
msgs = old_msgs;
|
|
mapOverMessages(in, f);
|
|
|
|
bytes = null;
|
|
bytebuf = null;
|
|
linebreak = null;
|
|
}
|
|
|
|
|
|
|
|
void recordOne(int envelope_start, int message_start,
|
|
int headers_end, int message_end,
|
|
InternetHeaders headers)
|
|
throws IOException {
|
|
|
|
// System.err.println(" ** " +
|
|
// envelope_start + " " + message_start + " " +
|
|
// headers_end + " " + message_end);
|
|
|
|
Assert.Assertion(envelope_start >= 0 &&
|
|
message_start > envelope_start &&
|
|
headers_end >= message_start &&
|
|
message_end >= headers_end);
|
|
|
|
BerkeleyMessage old_msg = (BerkeleyMessage) msgs.nextElement();
|
|
Assert.Assertion(old_msg != null);
|
|
|
|
if (DEBUG) {
|
|
// Make sure the sequence of messages in the file is in step with the
|
|
// sequence of messages in the folder object.
|
|
String idh = null;
|
|
String hh[] = headers.getHeader("Message-ID");
|
|
if (hh != null && hh.length != 0)
|
|
idh = hh[0];
|
|
|
|
if (idh != null) {
|
|
MessageID id = new MessageID(idh);
|
|
if (bytebuf.length() > 4 &&
|
|
!id.equals(old_msg.getMessageID())) {
|
|
System.err.println("ids differ!");
|
|
System.err.println(" new id = " + id + " (" + idh + ")");
|
|
System.err.println(" old id = " + old_msg.getMessageID());
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((old_msg.flags & MessageBase.FLAG_DELETED) != 0)
|
|
// This message is deleted; don't write it out.
|
|
return;
|
|
|
|
|
|
// Create a new message. We can't reuse the old message, because the
|
|
// caller is going to want to back out of any changes if an error occurs;
|
|
// so it needs to hold on to a copy of the original data until we're done.
|
|
//
|
|
BerkeleyMessage new_msg = new BerkeleyMessage(fFolder, headers);
|
|
|
|
|
|
// First, copy the envelope line from one file to the other.
|
|
// Then remember the position in the output file of (what will be) the
|
|
// beginning of the headers of this message.
|
|
//
|
|
long in_pos = in.getFilePointer();
|
|
|
|
in.seek(envelope_start);
|
|
copyBytes(message_start - envelope_start, false);
|
|
new_msg.setStorageFolderIndex((int) out.getFilePointer());
|
|
|
|
|
|
int total_msg_bytes_written = 0; // doesn't include envelope
|
|
|
|
|
|
// Now, write out the headers. Rather than just copying the bytes of
|
|
// the headers from one file to another, we reconstruct the output
|
|
// headers from the parsed headers object. We do this because it's
|
|
// the easiest way to skip over (don't copy) certain headers, which
|
|
// we want to recompute the values of.
|
|
//
|
|
// Note that this could possibly (but not probably) change the linebreaks
|
|
// of the headers during their passage from one file to another. Note
|
|
// also that we *don't* change the linebreaks of the bodies, just the
|
|
// headers. Since all linebreaks in one file should match, this
|
|
// shouldn't happen (or matter.)
|
|
//
|
|
for (Enumeration e = headers.getAllHeaders(); e.hasMoreElements();) {
|
|
Header hh = (Header) e.nextElement();
|
|
String h = hh.getName();
|
|
String v = hh.getValue();
|
|
|
|
// Don't copy these headers from input to output.
|
|
if (h.equalsIgnoreCase("X-Mozilla-Status") ||
|
|
h.equalsIgnoreCase("Content-Length") ||
|
|
h.equalsIgnoreCase("X-UIDL"))
|
|
continue;
|
|
|
|
bytebuf.setLength(0);
|
|
bytebuf.append(h);
|
|
bytebuf.append(": ");
|
|
bytebuf.append(v);
|
|
bytebuf.append(linebreak);
|
|
out.write(bytebuf.toBytes(), 0, bytebuf.length());
|
|
total_msg_bytes_written += bytebuf.length();
|
|
}
|
|
|
|
// Now, write out the recomputed values of the X-Mozilla-Status,
|
|
// Content-Length, and (maybe) X-UIDL headers.
|
|
//
|
|
bytebuf.setLength(0);
|
|
|
|
if (false) {
|
|
bytebuf.append("X-UIDL: ");
|
|
bytebuf.append("####");
|
|
bytebuf.append(linebreak);
|
|
}
|
|
|
|
// if (old_msg.flags != new_msg.flags) {
|
|
// System.err.println("flags diff at " + message_start + ": " +
|
|
// old_msg.flags + " " + new_msg.flags);
|
|
// }
|
|
|
|
long xms = BerkeleyMessage.makeMozillaFlags(old_msg);
|
|
bytebuf.append("X-Mozilla-Status: ");
|
|
String s = Long.toHexString(xms);
|
|
int i = s.length();
|
|
if (i == 1) bytebuf.append("000");
|
|
else if (i == 2) bytebuf.append("00");
|
|
else if (i == 3) bytebuf.append("0");
|
|
bytebuf.append(s);
|
|
bytebuf.append(linebreak);
|
|
|
|
bytebuf.append("Content-Length: ");
|
|
int cl = (message_end - headers_end - linebreak.length);
|
|
if (cl < 0) cl = 0;
|
|
bytebuf.append(cl);
|
|
bytebuf.append(linebreak);
|
|
|
|
out.write(bytebuf.toBytes(), 0, bytebuf.length());
|
|
total_msg_bytes_written += bytebuf.length();
|
|
|
|
|
|
// We've written the headers. Now copy the body bytes.
|
|
//
|
|
in.seek(headers_end);
|
|
copyBytes(message_end - headers_end, true);
|
|
total_msg_bytes_written += (message_end - headers_end);
|
|
in.seek(in_pos);
|
|
|
|
// Now that we've written the data, update the message with the true
|
|
// byte-length (this will be the value of the Content-Length header,
|
|
// plus the number of bytes we wrote for the headers.)
|
|
//
|
|
new_msg.setSize(total_msg_bytes_written);
|
|
|
|
// Update the summary counts...
|
|
Assert.Assertion(!new_msg.isDeleted());
|
|
total_message_count++;
|
|
undeleted_message_count++;
|
|
if (!new_msg.isRead())
|
|
unread_message_count++;
|
|
|
|
// Now hand this new message object back to the folder, and we're done.
|
|
//
|
|
fFolder.noticeInitialMessage(new_msg);
|
|
}
|
|
|
|
|
|
/** Copy N bytes from `in' to `out'.
|
|
If `blank_line' is true, and the data copied didn't end with a
|
|
linebreak, insert a linebreak.
|
|
*/
|
|
private void copyBytes(int n, boolean blank_line) throws IOException {
|
|
int buf_length = bytes.length;
|
|
int bytes_left = n;
|
|
boolean broken = false;
|
|
|
|
while (bytes_left > 0) {
|
|
int to_read = bytes_left;
|
|
if (to_read > buf_length)
|
|
to_read = buf_length;
|
|
int bytes_read = in.read(bytes, 0, to_read);
|
|
// System.err.println(" read " + bytes_read +
|
|
// " (with " + bytes_left + " left)");
|
|
if (bytes_read < 0)
|
|
throw new EOFException();
|
|
else
|
|
bytes_left -= bytes_read;
|
|
|
|
out.write(bytes, 0, bytes_read);
|
|
|
|
if (blank_line)
|
|
broken = (bytes[bytes_read-1] == '\r' ||
|
|
bytes[bytes_read-1] == '\n');
|
|
}
|
|
|
|
if (blank_line && !broken) {
|
|
// System.err.println("inventing linebreak at " + in.getFilePointer());
|
|
out.write(linebreak, 0, linebreak.length);
|
|
}
|
|
}
|
|
}
|