Package org.apache.james.mailbox.maildir.mail.model

Source Code of org.apache.james.mailbox.maildir.mail.model.MaildirMessage

/****************************************************************
* 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.                                           *
****************************************************************/
package org.apache.james.mailbox.maildir.mail.model;

import org.apache.commons.io.IOUtils;
import org.apache.james.mailbox.maildir.MaildirFolder;
import org.apache.james.mailbox.maildir.MaildirMessageName;
import org.apache.james.mailbox.store.mail.model.AbstractMessage;
import org.apache.james.mailbox.store.mail.model.Mailbox;
import org.apache.james.mailbox.store.mail.model.Property;
import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
import org.apache.james.mailbox.store.streaming.CountingInputStream;
import org.apache.james.mailbox.store.streaming.LimitingFileInputStream;
import org.apache.james.mime4j.MimeException;
import org.apache.james.mime4j.message.DefaultBodyDescriptorBuilder;
import org.apache.james.mime4j.message.MaximalBodyDescriptor;
import org.apache.james.mime4j.stream.EntityState;
import org.apache.james.mime4j.stream.MimeConfig;
import org.apache.james.mime4j.stream.MimeTokenStream;
import org.apache.james.mime4j.stream.RecursionMode;

import javax.mail.Flags;
import javax.mail.util.SharedFileInputStream;
import java.io.*;
import java.util.Date;
import java.util.List;

public class MaildirMessage extends AbstractMessage<Integer> {

    private MaildirMessageName messageName;
    private int bodyStartOctet;
    private final PropertyBuilder propertyBuilder = new PropertyBuilder();
    private boolean parsed;
    private boolean answered;
    private boolean deleted;
    private boolean draft;
    private boolean flagged;
    private boolean recent;
    private boolean seen;
    private Mailbox<Integer> mailbox;
    private long uid;
    protected boolean newMessage;
    private long modSeq;
   
    public MaildirMessage(Mailbox<Integer> mailbox, long uid, MaildirMessageName messageName) throws IOException {
        this.mailbox = mailbox;
        setUid(uid);
        setModSeq(messageName.getFile().lastModified());
        Flags flags = messageName.getFlags();
       
        // Set the flags for the message and respect if its RECENT
        // See MAILBOX-84
        File file = messageName.getFile();
        if (!file.exists()) {
            throw new FileNotFoundException("Unable to read file " + file.getAbsolutePath() + " for the message");
        } else {
            // if the message resist in the new folder its RECENT
            if (file.getParentFile().getName().equals(MaildirFolder.NEW)) {
                if (flags == null)
                    flags = new Flags();
                flags.add(Flags.Flag.RECENT);
            }
        }
        setFlags(flags);
        this.messageName = messageName;
    }

   
    @Override
    public Integer getMailboxId() {
        return mailbox.getMailboxId();
    }

    @Override
    public long getUid() {
        return uid;
    }

    @Override
    public void setUid(long uid) {
        this.uid = uid;
    }
    /**
     * @see
     * org.apache.james.mailbox.store.mail.model.Message#setFlags(
     * javax.mail.Flags)
     */
    @Override
    public void setFlags(Flags flags) {
        if (flags != null) {
            answered = flags.contains(Flags.Flag.ANSWERED);
            deleted = flags.contains(Flags.Flag.DELETED);
            draft = flags.contains(Flags.Flag.DRAFT);
            flagged = flags.contains(Flags.Flag.FLAGGED);
            recent = flags.contains(Flags.Flag.RECENT);
            seen = flags.contains(Flags.Flag.SEEN);
        }
    }
   
    /**
     * @see
     * org.apache.james.mailbox.store.mail.model.Message#isAnswered()
     */
    @Override
    public boolean isAnswered() {
        return answered;
    }

    /**
     * @see
     * org.apache.james.mailbox.store.mail.model.Message#isDeleted()
     */
    @Override
    public boolean isDeleted() {
        return deleted;
    }

    /**
     * @see
     * org.apache.james.mailbox.store.mail.model.Message#isDraft()
     */
    @Override
    public boolean isDraft() {
        return draft;
    }

    /**
     * @see
     * org.apache.james.mailbox.store.mail.model.Message#isFlagged()
     */
    @Override
    public boolean isFlagged() {
        return flagged;
    }

    /**
     * @see
     * org.apache.james.mailbox.store.mail.model.Message#isRecent()
     */
    @Override
    public boolean isRecent() {
        return recent;
    }

    /**
     * @see org.apache.james.mailbox.store.mail.model.Message#isSeen()
     */
    @Override
    public boolean isSeen() {
        return seen;
    }

    /**
     * Indicates whether this MaildirMessage reflects a new message or one that already
     * exists in the file system.
     * @return true if it is new, false if it already exists
     */
    public boolean isNew() {
        return newMessage;
    }
   
   
    @Override
    public String toString() {
        StringBuilder theString = new StringBuilder("MaildirMessage ");
        theString.append(getUid());
        theString.append(" {");
        Flags flags = createFlags();
        if (flags.contains(Flags.Flag.DRAFT))
            theString.append(MaildirMessageName.FLAG_DRAFT);
        if (flags.contains(Flags.Flag.FLAGGED))
            theString.append(MaildirMessageName.FLAG_FLAGGED);
        if (flags.contains(Flags.Flag.ANSWERED))
            theString.append(MaildirMessageName.FLAG_ANSWERD);
        if (flags.contains(Flags.Flag.SEEN))
            theString.append(MaildirMessageName.FLAG_SEEN);
        if (flags.contains(Flags.Flag.DELETED))
            theString.append(MaildirMessageName.FLAG_DELETED);
        theString.append("} ");
        theString.append(getInternalDate());
        return theString.toString();
    }

    /**
     * @see org.apache.james.mailbox.store.mail.model.Message#getModSeq()
     */
    @Override
    public long getModSeq() {
        return modSeq;
    }

    /**
     * @see org.apache.james.mailbox.store.mail.model.Message#setModSeq(long)
     */
    @Override
    public void setModSeq(long modSeq) {
        this.modSeq = modSeq;
    }
    /**
     * Parse message if needed
     */
    private synchronized void parseMessage() {
        if (parsed)
            return;
        SharedFileInputStream tmpMsgIn = null;
        try {
            tmpMsgIn = new SharedFileInputStream(messageName.getFile());

            bodyStartOctet = bodyStartOctet(tmpMsgIn);

            // Disable line length... This should be handled by the smtp server
            // component and not the parser itself
            // https://issues.apache.org/jira/browse/IMAP-122
            MimeConfig config = new MimeConfig();
            config.setMaxLineLen(-1);
            final MimeTokenStream parser = new MimeTokenStream(config, new DefaultBodyDescriptorBuilder());
            parser.setRecursionMode(RecursionMode.M_NO_RECURSE);
            parser.parse(tmpMsgIn.newStream(0, -1));

            EntityState next = parser.next();
            while (next != EntityState.T_BODY && next != EntityState.T_END_OF_STREAM && next != EntityState.T_START_MULTIPART) {
                next = parser.next();
            }
            final MaximalBodyDescriptor descriptor = (MaximalBodyDescriptor) parser.getBodyDescriptor();
            final String mediaType;
            final String mediaTypeFromHeader = descriptor.getMediaType();
            final String subType;
            if (mediaTypeFromHeader == null) {
                mediaType = "text";
                subType = "plain";
            } else {
                mediaType = mediaTypeFromHeader;
                subType = descriptor.getSubType();
            }
            propertyBuilder.setMediaType(mediaType);
            propertyBuilder.setSubType(subType);
            propertyBuilder.setContentID(descriptor.getContentId());
            propertyBuilder.setContentDescription(descriptor.getContentDescription());
            propertyBuilder.setContentLocation(descriptor.getContentLocation());
            propertyBuilder.setContentMD5(descriptor.getContentMD5Raw());
            propertyBuilder.setContentTransferEncoding(descriptor.getTransferEncoding());
            propertyBuilder.setContentLanguage(descriptor.getContentLanguage());
            propertyBuilder.setContentDispositionType(descriptor.getContentDispositionType());
            propertyBuilder.setContentDispositionParameters(descriptor.getContentDispositionParameters());
            propertyBuilder.setContentTypeParameters(descriptor.getContentTypeParameters());
            // Add missing types
            final String codeset = descriptor.getCharset();
            if (codeset == null) {
                if ("TEXT".equalsIgnoreCase(mediaType)) {
                    propertyBuilder.setCharset("us-ascii");
                }
            } else {
                propertyBuilder.setCharset(codeset);
            }

            final String boundary = descriptor.getBoundary();
            if (boundary != null) {
                propertyBuilder.setBoundary(boundary);
            }
            if ("text".equalsIgnoreCase(mediaType)) {
                long lines = -1;
                final CountingInputStream bodyStream = new CountingInputStream(parser.getInputStream());
                try {
                    bodyStream.readAll();
                    lines = bodyStream.getLineCount();
                } finally {
                    IOUtils.closeQuietly(bodyStream);
                }

                next = parser.next();
                if (next == EntityState.T_EPILOGUE) {
                    final CountingInputStream epilogueStream = new CountingInputStream(parser.getInputStream());
                    try {
                        epilogueStream.readAll();
                        lines += epilogueStream.getLineCount();
                    } finally {
                        IOUtils.closeQuietly(epilogueStream);
                    }
                }
                propertyBuilder.setTextualLineCount(lines);
            }
        } catch (IOException e) {
            // has successfully been parsen when appending, shouldn't give any
            // problems
        } catch (MimeException e) {
            // has successfully been parsen when appending, shouldn't give any
            // problems
        } finally {
            if (tmpMsgIn != null) {
                try {
                    tmpMsgIn.close();
                } catch (IOException e) {
                    // ignore on close
                }
            }
            parsed = true;
        }
    }

    /**
     * Return the position in the given {@link InputStream} at which the Body of
     * the Message starts
     *
     * @param msgIn
     * @return bodyStartOctet
     * @throws IOException
     */
    private int bodyStartOctet(InputStream msgIn) throws IOException {
        // we need to pushback maximal 3 bytes
        PushbackInputStream in = new PushbackInputStream(msgIn, 3);
        int localBodyStartOctet = in.available();
        int i = -1;
        int count = 0;
        while ((i = in.read()) != -1 && in.available() > 4) {
            if (i == 0x0D) {
                int a = in.read();
                if (a == 0x0A) {
                    int b = in.read();

                    if (b == 0x0D) {
                        int c = in.read();

                        if (c == 0x0A) {
                            localBodyStartOctet = count + 4;
                            break;
                        }
                        in.unread(c);
                    }
                    in.unread(b);
                }
                in.unread(a);
            }
            count++;
        }
        return localBodyStartOctet;
    }

    /**
     * @see org.apache.james.mailbox.store.mail.model.Message#getMediaType()
     */
    @Override
    public String getMediaType() {
        parseMessage();
        return propertyBuilder.getMediaType();
    }

    /**
     * @see org.apache.james.mailbox.store.mail.model.Message#getSubType()
     */
    @Override
    public String getSubType() {
        parseMessage();
        return propertyBuilder.getSubType();
    }

    /**
     * @see org.apache.james.mailbox.store.mail.model.Message#getFullContentOctets()
     */
    @Override
    public long getFullContentOctets() {
        Long size = messageName.getSize();
        if (size != null) {
            return size;
        } else {
            try {
                return messageName.getFile().length();
            } catch (FileNotFoundException e) {
                return -1;
            }
        }
    }

    /**
     * @see org.apache.james.mailbox.store.mail.model.Message#getTextualLineCount()
     */
    @Override
    public Long getTextualLineCount() {
        parseMessage();
        return propertyBuilder.getTextualLineCount();
    }

    /**
     * @see org.apache.james.mailbox.store.mail.model.Message#getProperties()
     */
    @Override
    public List<Property> getProperties() {
        parseMessage();
        return propertyBuilder.toProperties();
    }

    /**
     * @see org.apache.james.mailbox.store.mail.model.Message#getInternalDate()
     */
    @Override
    public Date getInternalDate() {
        return messageName.getInternalDate();
    }

    /**
     * Return the full content of the message via a {@link FileInputStream}
     */
    @Override
    public InputStream getFullContent() throws IOException {
        return new FileInputStream(messageName.getFile());
    }

    /**
     * @see org.apache.james.mailbox.store.mail.model.Message#getBodyContent()
     */
    @Override
    public InputStream getBodyContent() throws IOException {
        parseMessage();
        FileInputStream body = new FileInputStream(messageName.getFile());
        IOUtils.skipFully(body, bodyStartOctet);
        return body;

    }

    /**
     * @see org.apache.james.mailbox.store.mail.model.AbstractMessage#getBodyStartOctet()
     */
    @Override
    protected int getBodyStartOctet() {
        parseMessage();
        return bodyStartOctet;
    }

    @Override
    public InputStream getHeaderContent() throws IOException {
        parseMessage();
        long limit = getBodyStartOctet();
        if (limit < 0) {
            limit = 0;
        }
        return new LimitingFileInputStream(messageName.getFile(), limit);

    }


}
TOP

Related Classes of org.apache.james.mailbox.maildir.mail.model.MaildirMessage

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.