You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-user@james.apache.org by Norman <nm...@spam-box.de> on 2006/12/17 10:39:35 UTC
****-SPAM-**** Re: mboxrd support
Hi Philip,
i opened a bug report:
http://issues.apache.org/jira/browse/JAMES-744
Thx for reporting.
bye
Norman
Philip Tomlinson schrieb:
> Hi,
>
> I think I've answered my own question and discovered a bug with
> MBoxMailRepository and a fix is below.
>
> Line 283
> was:
> prevMessageStart = ins.getFilePointer() - line.length() ;
> should be:
> prevMessageStart = ins.getFilePointer() - line.length() - 2;
> This is the case because we need to subtract -1 to back up in front of
> the character we are looking for and -1 because the readLine doesnt
> return the carriage return
> Now the preMessageStart points to the F character in the pattern From
> .......
>
> and line 413
> was:
> ins.seek(messageStart -1);
> should be:
> ins.seek(messageStart );
> Given that the messageStart now points at the F character there is no
> longer a need to back up one character.
>
> The full file is attached.
>
> Now I can parse Thunderbird mail files without a problem
>
> Rgds,
> Phil
>
>> I've been trying to use the MBoxMailRepository to parse a Thunderbird
>> mail file.
>> It doesnt seem to work propely. (After loading the messages, the
>> retrieve operation quite often doesnt return a message for a key).
>>
>> Is this because the MBoxMailRepository class doesnt support mboxrd?
>>
> -------------------------
> Email: phil@ez123.co.nz
>
>
> ------------------------------------------------------------------------
>
> /****************************************************************
> * Licensed to the Apache Software Foundation (ASF) under one *
> * or more contributor license agreements. See the NOTICE file *
> * distributed with this work for additional information *
> * regarding copyright ownership. The ASF licenses this file *
> * to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 *
> * *
> * Unless required by applicable law or agreed to in writing, *
> * software distributed under the License is distributed on an *
> * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
> * KIND, either express or implied. See the License for the *
> * specific language governing permissions and limitations *
> * under the License. *
> ****************************************************************/
>
>
>
> /* TODO:
> *
> * 1. Currently, iterating through the message collection does not
> * preserve the order in the file. Change this with some form of
> * OrderedMap. There is a suitable class in Jakarta Commons
> * Collections.
> *
> * 2. Optimize the remove operation.
> *
> * 3. Don't load entire message into memory. This would mean computing
> * the hash during I/O streaming, rather than loading entire message
> * into memory, and using a MimeMessageWrapper with a suitable data
> * source. As a strawman, the interface to MessageAction would
> * carry the hash, along with a size-limited stream providing the
> * message body.
> *
> * 4. Decide what to do when there are IDENTICAL messages in the file.
> * Right now only the last one will ever be processed, due to key
> * collissions.
> *
> * 5. isComplete() - DONE.
> *
> * 6. Buffered I/O. - Partially done, and optional.
> *
> */
>
> package org.apache.james.mailrepository;
>
> import org.apache.avalon.framework.configuration.Configurable;
> import org.apache.avalon.framework.configuration.Configuration;
> import org.apache.avalon.framework.configuration.ConfigurationException;
> import org.apache.avalon.framework.logger.AbstractLogEnabled;
> import org.apache.james.core.MailImpl;
> import org.apache.james.services.MailRepository;
> import org.apache.mailet.Mail;
> import org.apache.oro.text.regex.MalformedPatternException;
> import org.apache.oro.text.regex.Perl5Compiler;
> import org.apache.oro.text.regex.Pattern;
> import org.apache.oro.text.regex.Perl5Matcher;
>
> import javax.mail.MessagingException;
> import javax.mail.Session;
> import javax.mail.internet.MimeMessage;
>
> import java.io.ByteArrayInputStream;
> import java.io.ByteArrayOutputStream;
> import java.io.File;
> import java.io.FileNotFoundException;
> import java.io.IOException;
> import java.io.RandomAccessFile;
> import java.security.NoSuchAlgorithmException;
> import java.security.MessageDigest;
> import java.text.SimpleDateFormat;
> import java.util.ArrayList;
> import java.util.Calendar;
> import java.util.Collection;
> import java.util.Collections;
> import java.util.Hashtable;
> import java.util.Iterator;
> import java.util.Locale;
> import java.util.Properties;
> import java.util.Vector;
>
> /**
> * Implementation of a MailRepository using UNIX mbox files.
> *
> * <p>Requires a configuration element in the .conf.xml file of the form:
> * <br><repository destinationURL="mbox://<directory>"
> * <br> type="MAIL"
> * <br></directory> is where the individual mbox files are read from/written to
> * <br>Type can ONLY be MAIL (SPOOL is NOT supported)
> *
> * <p>Requires a logger called MailRepository.
> *
> * <p> Implementation notes:
> * <p>
> * This class keeps an internal store of the mbox file
> * When the internal mbox file is updated (added/deleted)
> * then the file will be re-read from disk and then written back.
> * This is a bit inefficent but means that the file on disk
> * should be correct.
> * <p>
> * The mbox store is mainly meant to be used as a one-way street.
> * Storing new emails is very fast (append to file) whereas reading them (via POP3) is
> * slower (read from disk and parse).
> * Therefore this implementation is best suited to people who wish to use the mbox format
> * for taking data out of James and into something else (IMAP server or mail list displayer)
> *
> * @version CVS $Revision: 453945 $
> */
>
>
> public class MBoxMailRepository
> extends AbstractLogEnabled
> implements MailRepository, Configurable {
>
>
> static final SimpleDateFormat dy = new SimpleDateFormat("EE MMM dd HH:mm:ss yyyy", Locale.US);
> static final String LOCKEXT = ".lock";
> static final String WORKEXT = ".work";
> static final int LOCKSLEEPDELAY = 2000; // 2 second back off in the event of a problem with the lock file
> static final int MAXSLEEPTIMES = 100; //
> static final long MLISTPRESIZEFACTOR = 10 * 1024; // The hash table will be loaded with a initial capacity of filelength/MLISTPRESIZEFACTOR
> static final long DEFAULTMLISTCAPACITY = 20; // Set up a hashtable to have a meaningful default
>
> /**
> * Whether line buffering is turned used.
> */
> private static boolean BUFFERING = true;
>
> /**
> * Whether 'deep debugging' is turned on.
> */
> private static final boolean DEEP_DEBUG = true;
>
> /**
> * The internal list of the emails
> * The key is an adapted MD5 checksum of the mail
> */
> private Hashtable mList = null;
> /**
> * The filename to read & write the mbox from/to
> */
> private String mboxFile;
>
> private boolean fifo;
>
> /**
> * A callback used when a message is read from the mbox file
> */
> public interface MessageAction {
> public boolean isComplete(); // *** Not valid until AFTER each call to messageAction(...)!
> public MimeMessage messageAction(String messageSeparator, String bodyText, long messageStart);
> }
>
>
> /**
> * Convert a MimeMessage into raw text
> * @param mc The mime message to convert
> * @return A string representation of the mime message
> * @throws IOException
> * @throws MessagingException
> */
> private String getRawMessage(MimeMessage mc) throws IOException, MessagingException {
>
> ByteArrayOutputStream rawMessage = new ByteArrayOutputStream();
> mc.writeTo(rawMessage);
> return rawMessage.toString();
> }
>
> /**
> * Parse a text block as an email and convert it into a mime message
> * @param emailBody The headers and body of an email. This will be parsed into a mime message and stored
> */
> private MimeMessage convertTextToMimeMessage(String emailBody) {
> //this.emailBody = emailBody;
> MimeMessage mimeMessage = null;
> // Parse the mime message as we have the full message now (in string format)
> ByteArrayInputStream mb = new ByteArrayInputStream(emailBody.getBytes());
> Properties props = System.getProperties();
> Session session = Session.getDefaultInstance(props);
> try {
> mimeMessage = new MimeMessage(session, mb);
>
>
> } catch (MessagingException e) {
> getLogger().error("Unable to parse mime message!", e);
> }
>
> if (mimeMessage == null && getLogger().isDebugEnabled()) {
> StringBuffer logBuffer =
> new StringBuffer(128)
> .append(this.getClass().getName())
> .append(" Mime message is null");
> getLogger().debug(logBuffer.toString());
> }
>
> /*
> String toAddr = null;
> try {
> // Attempt to read the TO field and see if it errors
> toAddr = mimeMessage.getRecipients(javax.mail.Message.RecipientType.TO).toString();
> } catch (Exception e) {
> // It has errored, so time for plan B
> // use the from field I suppose
> try {
> mimeMessage.setRecipients(javax.mail.Message.RecipientType.TO, mimeMessage.getFrom());
> if (getLogger().isDebugEnabled()) {
> StringBuffer logBuffer =
> new StringBuffer(128)
> .append(this.getClass().getName())
> .append(" Patching To: field for message ")
> .append(" with From: field");
> getLogger().debug(logBuffer.toString());
> }
> } catch (MessagingException e1) {
> getLogger().error("Unable to set to: field to from: field", e);
> }
> } */
> return mimeMessage;
> }
>
> /**
> * Generate a hex representation of an MD5 checksum on the emailbody
> * @param emailBody
> * @return A hex representation of the text
> * @throws NoSuchAlgorithmException
> */
> private String generateKeyValue(String emailBody) throws NoSuchAlgorithmException {
> // MD5 the email body for a reilable (ha ha) key
> byte[] digArray = MessageDigest.getInstance("MD5").digest(emailBody.getBytes());
> StringBuffer digest = new StringBuffer();
> for (int i = 0; i < digArray.length; i++) {
> digest.append(Integer.toString(digArray[i], Character.MAX_RADIX).toUpperCase(Locale.US));
> }
> return digest.toString();
> }
>
> /**
> * Parse the mbox file.
> * @param ins The random access file to load. Note that the file may or may not start at offset 0 in the file
> * @param messAct The action to take when a message is found
> */
> private MimeMessage parseMboxFile(RandomAccessFile ins, MessageAction messAct) {
> if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
> StringBuffer logBuffer =
> new StringBuffer(128)
> .append(this.getClass().getName())
> .append(" Start parsing ")
> .append(mboxFile);
>
> getLogger().debug(logBuffer.toString());
> }
> try {
>
> Perl5Compiler sepMatchCompiler = new Perl5Compiler();
> Pattern sepMatchPattern = sepMatchCompiler.compile("^From (.*) (.*):(.*):(.*)$");
> Perl5Matcher sepMatch = new Perl5Matcher();
>
> int c;
> boolean inMessage = false;
> StringBuffer messageBuffer = new StringBuffer();
> String previousMessageSeparator = null;
> boolean foundSep = false;
>
> long prevMessageStart = ins.getFilePointer();
> if (BUFFERING) {
> String line = null;
> while ((line = ins.readLine()) != null) {
> foundSep = sepMatch.contains(line + "\n", sepMatchPattern);
>
> if (foundSep && inMessage) {
> // if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
> // getLogger().debug(this.getClass().getName() + " Invoking " + messAct.getClass() + " at " + prevMessageStart);
> // }
> MimeMessage endResult = messAct.messageAction(previousMessageSeparator, messageBuffer.toString(), prevMessageStart);
> if (messAct.isComplete()) {
> // I've got what I want so just exit
> return endResult;
> }
> previousMessageSeparator = line;
> prevMessageStart = ins.getFilePointer() - line.length() - 2;
> messageBuffer = new StringBuffer();
> inMessage = true;
> }
> // Only done at the start (first header)
> if (foundSep && !inMessage) {
> previousMessageSeparator = line.toString();
> inMessage = true;
> }
> if (!foundSep && inMessage) {
> messageBuffer.append(line).append("\n");
> }
> }
> } else {
> StringBuffer line = new StringBuffer();
> while ((c = ins.read()) != -1) {
> if (c == 10) {
> foundSep = sepMatch.contains(line.toString(), sepMatchPattern);
> if (foundSep && inMessage) {
> // if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
> // getLogger().debug(this.getClass().getName() + " Invoking " + messAct.getClass() + " at " + prevMessageStart);
> // }
> MimeMessage endResult = messAct.messageAction(previousMessageSeparator, messageBuffer.toString(), prevMessageStart);
> if (messAct.isComplete()) {
> // I've got what I want so just exit
> return endResult;
> }
> previousMessageSeparator = line.toString();
> prevMessageStart = ins.getFilePointer() - line.length();
> messageBuffer = new StringBuffer();
> inMessage = true;
> }
> // Only done at the start (first header)
> if (foundSep && inMessage == false) {
> previousMessageSeparator = line.toString();
> inMessage = true;
> }
> if (!foundSep) {
> messageBuffer.append(line).append((char) c);
> }
> line = new StringBuffer(); // Reset buffer
> } else {
> line.append((char) c);
> }
> }
> }
>
> if (messageBuffer.length() != 0) {
> // process last message
> return messAct.messageAction(previousMessageSeparator, messageBuffer.toString(), prevMessageStart);
> }
> } catch (IOException ioEx) {
> getLogger().error("Unable to write file (General I/O problem) " + mboxFile, ioEx);
> } catch (MalformedPatternException e) {
> getLogger().error("Bad regex passed " + mboxFile, e);
> } finally {
> if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
> StringBuffer logBuffer =
> new StringBuffer(128)
> .append(this.getClass().getName())
> .append(" Finished parsing ")
> .append(mboxFile);
>
> getLogger().debug(logBuffer.toString());
> }
> }
> return null;
> }
>
> /**
> * Find a given message
> * This method will first use selectMessage(key) to see if the key/offset combination allows us to skip
> * parts of the file and only load the message we are interested in
> *
> * @param key The key of the message to find
> */
> private MimeMessage findMessage(String key) {
> MimeMessage foundMessage = null;
>
> // See if we can get the message by using the cache position first
> foundMessage = selectMessage(key);
> if (foundMessage == null) {
> // If the message is not found something has changed from
> // the cache. The cache may have been invalidated by
> // another method, or the file may have been replaced from
> // underneath us. Reload the cache, and try again.
> mList = null;
> loadKeys();
> foundMessage = selectMessage(key);
> }
> return foundMessage;
> }
>
> /**
> * Quickly find a message by using the stored message offsets
> * @param key The key of the message to find
> */
> private MimeMessage selectMessage(final String key) {
> MimeMessage foundMessage = null;
> // Can we find the key first
> if (mList == null || !mList.containsKey(key)) {
> // Not initiailised so no point looking
> if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
> StringBuffer logBuffer =
> new StringBuffer(128)
> .append(this.getClass().getName())
> .append(" mList - key not found ")
> .append(mboxFile);
>
> getLogger().debug(logBuffer.toString());
> }
> return foundMessage;
> }
> long messageStart = ((Long) mList.get(key)).longValue();
> if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
> StringBuffer logBuffer =
> new StringBuffer(128)
> .append(this.getClass().getName())
> .append(" Load message starting at offset ")
> .append(messageStart)
> .append(" from file ")
> .append(mboxFile);
>
> getLogger().debug(logBuffer.toString());
> }
> // Now try and find the position in the file
> RandomAccessFile ins = null;
> try {
> ins = new RandomAccessFile(mboxFile, "r");
> if (messageStart != 0) {
> ins.seek(messageStart );
> }
> MessageAction op = new MessageAction() {
> public boolean isComplete() { return true; }
> public MimeMessage messageAction(String messageSeparator, String bodyText, long messageStart) {
> try {
> if (key.equals(generateKeyValue(bodyText))) {
> getLogger().debug(this.getClass().getName() + " Located message. Returning MIME message");
> return convertTextToMimeMessage(bodyText);
> }
> } catch (NoSuchAlgorithmException e) {
> getLogger().error("MD5 not supported! ",e);
> }
> return null;
> }
> };
> foundMessage = this.parseMboxFile(ins, op);
> } catch (FileNotFoundException e) {
> getLogger().error("Unable to save(open) file (File not found) " + mboxFile, e);
> } catch (IOException e) {
> getLogger().error("Unable to write file (General I/O problem) " + mboxFile, e);
> } finally {
> if (foundMessage == null) {
> if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
> StringBuffer logBuffer =
> new StringBuffer(128)
> .append(this.getClass().getName())
> .append(" select - message not found ")
> .append(mboxFile);
>
> getLogger().debug(logBuffer.toString());
> }
> }
> if (ins != null) try { ins.close(); } catch (IOException e) { getLogger().error("Unable to close file (General I/O problem) " + mboxFile, e); }
> }
> return foundMessage;
> }
>
> /**
> * Load the message keys and file pointer offsets from disk
> */
> private synchronized void loadKeys() {
> if (mList!=null) {
> return;
> }
> RandomAccessFile ins = null;
> try {
> ins = new RandomAccessFile(mboxFile, "r");
> long initialCapacity = (ins.length() > MLISTPRESIZEFACTOR ? ins.length() /MLISTPRESIZEFACTOR : 0);
> if (initialCapacity < DEFAULTMLISTCAPACITY ) {
> initialCapacity = DEFAULTMLISTCAPACITY;
> }
> if (initialCapacity > Integer.MAX_VALUE) {
> initialCapacity = Integer.MAX_VALUE - 1;
> }
> this.mList = new Hashtable((int)initialCapacity);
> this.parseMboxFile(ins, new MessageAction() {
> public boolean isComplete() { return false; }
> public MimeMessage messageAction(String messageSeparator, String bodyText, long messageStart) {
> try {
> String key = generateKeyValue(bodyText);
> mList.put(key, new Long(messageStart));
> if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
> getLogger().debug(this.getClass().getName() + " Key " + key + " at " + messageStart);
> }
>
> } catch (NoSuchAlgorithmException e) {
> getLogger().error("MD5 not supported! ",e);
> }
> return null;
> }
> });
> //System.out.println("Done Load keys!");
> } catch (FileNotFoundException e) {
> getLogger().error("Unable to save(open) file (File not found) " + mboxFile, e);
> this.mList = new Hashtable((int)DEFAULTMLISTCAPACITY);
> } catch (IOException e) {
> getLogger().error("Unable to write file (General I/O problem) " + mboxFile, e);
> } finally {
> if (ins != null) try { ins.close(); } catch (IOException e) { getLogger().error("Unable to close file (General I/O problem) " + mboxFile, e); }
> }
> }
>
>
> /**
> * @see org.apache.james.services.MailRepository#store(Mail)
> */
> public void store(Mail mc) {
>
> if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
> StringBuffer logBuffer =
> new StringBuffer(128)
> .append(this.getClass().getName())
> .append(" Will store message to file ")
> .append(mboxFile);
>
> getLogger().debug(logBuffer.toString());
> }
> this.mList = null;
> // Now make up the from header
> String fromHeader = null;
> String message = null;
> try {
> message = getRawMessage(mc.getMessage());
> // check for nullsender
> if (mc.getMessage().getFrom() == null) {
> fromHeader = "From " + dy.format(Calendar.getInstance().getTime());
> } else {
> fromHeader = "From " + mc.getMessage().getFrom()[0] + " " + dy.format(Calendar.getInstance().getTime());
> }
>
> } catch (IOException e) {
> getLogger().error("Unable to parse mime message for " + mboxFile, e);
> } catch (MessagingException e) {
> getLogger().error("Unable to parse mime message for " + mboxFile, e);
> }
> // And save only the new stuff to disk
> RandomAccessFile saveFile = null;
> try {
> saveFile = new RandomAccessFile(mboxFile, "rw");
> saveFile.seek(saveFile.length()); // Move to the end
> saveFile.writeBytes((fromHeader + "\n"));
> saveFile.writeBytes((message + "\n"));
> saveFile.close();
>
> } catch (FileNotFoundException e) {
> getLogger().error("Unable to save(open) file (File not found) " + mboxFile, e);
> } catch (IOException e) {
> getLogger().error("Unable to write file (General I/O problem) " + mboxFile, e);
> }
> }
>
>
> /**
> * @see org.apache.james.services.MailRepository#list()
> */
> public Iterator list() {
> loadKeys();
> ArrayList keys = new ArrayList(mList.keySet());
>
> if (keys.isEmpty() == false) {
> // find the first message. This is a trick to make sure that if
> // the file is changed out from under us, we will detect it and
> // correct for it BEFORE we return the iterator.
> findMessage((String) keys.iterator().next());
> }
> if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
> StringBuffer logBuffer =
> new StringBuffer(128)
> .append(this.getClass().getName())
> .append(" ")
> .append(keys.size())
> .append(" keys to be iterated over.");
>
> getLogger().debug(logBuffer.toString());
> }
> if (fifo) Collections.sort(keys); // Keys is a HashSet; impose FIFO for apps that need it
> return keys.iterator();
> }
>
> /**
> * @see org.apache.james.services.MailRepository#retrieve(String)
> */
> public Mail retrieve(String key) {
>
> loadKeys();
> MailImpl res = null;
> try {
> MimeMessage foundMessage = findMessage(key);
> if (foundMessage == null) {
> getLogger().error("found message is null!");
> return null;
> }
> res = new MailImpl(foundMessage);
> res.setName(key);
> if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
> StringBuffer logBuffer =
> new StringBuffer(128)
> .append(this.getClass().getName())
> .append(" Retrieving entry for key ")
> .append(key);
>
> getLogger().debug(logBuffer.toString());
> }
> } catch (MessagingException e) {
> getLogger().error("Unable to parse mime message for " + mboxFile + "\n" + e.getMessage(), e);
> }
> return res;
> }
>
> /**
> * @see org.apache.james.services.MailRepository#remove(Mail)
> */
> public void remove(Mail mail) {
> // Convert the message into a key
> Vector delVec = new Vector();
> delVec.addElement(mail);
> remove(delVec);
> }
>
> /**
> * Attempt to get a lock on the mbox by creating
> * the file mboxname.lock
> * @throws Exception
> */
> private void lockMBox() throws Exception {
> // Create the lock file (if possible)
> String lockFileName = mboxFile + LOCKEXT;
> int sleepCount = 0;
> File mBoxLock = new File(lockFileName);
> if (!mBoxLock.createNewFile()) {
> // This is not good, somebody got the lock before me
> // So wait for a file
> while (!mBoxLock.createNewFile() && sleepCount < MAXSLEEPTIMES) {
> try {
> if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
> StringBuffer logBuffer =
> new StringBuffer(128)
> .append(this.getClass().getName())
> .append(" Waiting for lock on file ")
> .append(mboxFile);
>
> getLogger().debug(logBuffer.toString());
> }
>
> Thread.sleep(LOCKSLEEPDELAY);
> sleepCount++;
> } catch (InterruptedException e) {
> getLogger().error("File lock wait for " + mboxFile + " interrupted!",e);
>
> }
> }
> if (sleepCount >= MAXSLEEPTIMES) {
> throw new Exception("Unable to get lock on file " + mboxFile);
> }
> }
> }
>
> /**
> * Unlock a previously locked mbox file
> */
> private void unlockMBox() {
> // Just delete the MBOX file
> String lockFileName = mboxFile + LOCKEXT;
> File mBoxLock = new File(lockFileName);
> if (!mBoxLock.delete()) {
> StringBuffer logBuffer =
> new StringBuffer(128)
> .append(this.getClass().getName())
> .append(" Failed to delete lock file ")
> .append(lockFileName);
> getLogger().error(logBuffer.toString());
> }
> }
>
>
>
> /**
> * @see org.apache.james.services.MailRepository#remove(Collection)
> */
> public void remove(final Collection mails)
> {
> if ((DEEP_DEBUG) && (getLogger().isDebugEnabled())) {
> StringBuffer logBuffer =
> new StringBuffer(128)
> .append(this.getClass().getName())
> .append(" Removing entry for key ")
> .append(mails);
>
> getLogger().debug(logBuffer.toString());
> }
> // The plan is as follows:
> // Attempt to locate the message in the file
> // by reading through the
> // once we've done that then seek to the file
> try {
> RandomAccessFile ins = new RandomAccessFile(mboxFile, "r"); // The source
> final RandomAccessFile outputFile = new RandomAccessFile(mboxFile + WORKEXT, "rw"); // The destination
> parseMboxFile(ins, new MessageAction() {
> public boolean isComplete() { return false; }
> public MimeMessage messageAction(String messageSeparator, String bodyText, long messageStart) {
> // Write out the messages as we go, until we reach the key we want
> try {
> String currentKey=generateKeyValue(bodyText);
> boolean foundKey=false;
> Iterator mailList = mails.iterator();
> String key;
> while (mailList.hasNext()) {
> // Attempt to find the current key in the array
> key = ((Mail)mailList.next()).getName();
> if (key.equals(currentKey)) {
> // Don't write the message to disk
> foundKey = true;
> break;
> }
> }
> if (foundKey == false)
> {
> // We didn't find the key in the array so we will keep it
> outputFile.writeBytes(messageSeparator + "\n");
> outputFile.writeBytes(bodyText);
>
> }
> } catch (NoSuchAlgorithmException e) {
> getLogger().error("MD5 not supported! ",e);
> } catch (IOException e) {
> getLogger().error("Unable to write file (General I/O problem) " + mboxFile, e);
> }
> return null;
> }
> });
> ins.close();
> outputFile.close();
> // Delete the old mbox file
> File mbox = new File(mboxFile);
> mbox.delete();
> // And rename the lock file to be the new mbox
> mbox = new File(mboxFile + WORKEXT);
> if (!mbox.renameTo(new File(mboxFile)))
> {
> System.out.println("Failed to rename file!");
> }
>
> // Now delete the keys in mails from the main hash
> Iterator mailList = mails.iterator();
> String key;
> while (mailList.hasNext()) {
> // Attempt to find the current key in the array
> key = ((Mail)mailList.next()).getName();
> mList.remove(key);
> }
>
>
> } catch (FileNotFoundException e) {
> getLogger().error("Unable to save(open) file (File not found) " + mboxFile, e);
> } catch (IOException e) {
> getLogger().error("Unable to write file (General I/O problem) " + mboxFile, e);
> }
> }
>
> /**
> * @see org.apache.james.services.MailRepository#remove(String)
> */
> public void remove(String key) {
> loadKeys();
> try {
> lockMBox();
> } catch (Exception e) {
> getLogger().error("Lock failed!",e);
> return; // No lock, so exit
> }
> ArrayList keys = new ArrayList();
> keys.add(key);
>
> this.remove(keys);
> unlockMBox();
> }
>
> /**
> * @see org.apache.james.services.MailRepository#lock(String)
> */
> public boolean lock(String key) {
> return false;
> }
>
> /**
> * @see org.apache.james.services.MailRepository#unlock(String)
> */
> public boolean unlock(String key) {
> return false;
> }
>
> /**
> * @see org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
> */
> public void configure(Configuration conf) throws ConfigurationException {
> String destination;
> this.mList = null;
> BUFFERING = conf.getAttributeAsBoolean("BUFFERING", true);
> fifo = conf.getAttributeAsBoolean("FIFO", false);
> destination = conf.getAttribute("destinationURL");
> if (destination.charAt(destination.length() - 1) == '/') {
> // Remove the trailing / as well as the protocol marker
> mboxFile = destination.substring("mbox://".length(), destination.lastIndexOf("/"));
> } else {
> mboxFile = destination.substring("mbox://".length());
> }
>
> if (getLogger().isDebugEnabled()) {
> getLogger().debug("MBoxMailRepository.destinationURL: " + destination);
> }
>
> String checkType = conf.getAttribute("type");
> if (!(checkType.equals("MAIL") || checkType.equals("SPOOL"))) {
> String exceptionString = "Attempt to configure MboxMailRepository as " + checkType;
> if (getLogger().isWarnEnabled()) {
> getLogger().warn(exceptionString);
> }
> throw new ConfigurationException(exceptionString);
> }
> }
>
> }
>
>
> ------------------------------------------------------------------------
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: server-user-unsubscribe@james.apache.org
> For additional commands, e-mail: server-user-help@james.apache.org
---------------------------------------------------------------------
To unsubscribe, e-mail: server-user-unsubscribe@james.apache.org
For additional commands, e-mail: server-user-help@james.apache.org