/*
 * Copyright 2006-2007 Queplix Corp.
 *
 * Licensed under the Queplix Public License, Version 1.1.1 (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.queplix.com/solutions/commercial-open-source/queplix-public-license/
 *
 * 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 com.queplix.core.modules.inbox.ejb;

import com.queplix.core.error.GenericSystemException;
import com.queplix.core.integrator.security.LogonSession;
import com.queplix.core.modules.attachment.ejb.AttachmentManagerLocal;
import com.queplix.core.modules.attachment.ejb.AttachmentManagerLocalHome;
import com.queplix.core.modules.eql.error.EQLException;
import com.queplix.core.modules.inbox.InboxHelper;
import com.queplix.core.modules.inbox.InboxMessage;
import com.queplix.core.modules.inbox.utils.db.DBRealmManager;
import com.queplix.core.modules.inbox.utils.log.InboxLogPublisher;
import com.queplix.core.modules.inbox.utils.log.OutboxLogPublisher;
import com.queplix.core.modules.jeo.JEObjectHandler;
import com.queplix.core.modules.jeo.ejb.JEOManagerLocal;
import com.queplix.core.modules.jeo.ejb.JEOManagerLocalHome;
import com.queplix.core.modules.jeo.gen.AttachmentObject;
import com.queplix.core.modules.jeo.gen.AttachmentObjectHandler;
import com.queplix.core.modules.jeo.gen.AttachmentObjectsObject;
import com.queplix.core.modules.jeo.gen.AttachmentObjectsObjectHandler;
import com.queplix.core.modules.jeo.gen.AttachmentTempObject;
import com.queplix.core.modules.jeo.gen.InboxObject;
import com.queplix.core.modules.jeo.gen.InboxObjectHandler;
import com.queplix.core.modules.mail.Attachment;
import com.queplix.core.modules.mail.MailAddress;
import com.queplix.core.modules.mail.ejb.MailManagerLocal;
import com.queplix.core.modules.mail.ejb.MailManagerLocalHome;
import com.queplix.core.modules.eqlext.utils.FileManager;
import com.queplix.core.utils.DateHelper;
import com.queplix.core.utils.JNDINames;
import com.queplix.core.utils.ejb.AbstractSessionEJB;
import com.queplix.core.utils.sql.SqlWrapper;
import com.queplix.core.utils.sql.SqlWrapperFactory;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import org.apache.regexp.RE;
import org.apache.regexp.RESyntaxException;

import javax.mail.MessagingException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.io.File;

/**
 * The Inbox Manager EJB.
 *
 * @author Konstantin Mironov
 * @since 8 Dec 2006
 */
public class InboxManagerEJB extends AbstractSessionEJB {

    // ------------------------------------------------------- EJB API methods

    /**
     * Initializes bean.
     */
    public void ejbCreate() {
        INFO("InboxManager EJB created - " + hashCode());
    }

    // ------------------------------------------------------- Public methods

    /**
     * Generates new unique message ID.
     *
     * @return long
     */
    public long getUniqueMessageID() {

        long id;

        // SQL initialization.
        String tableName = DBRealmManager.getSql("get_inbox_table");
        SqlWrapper sqlWrapper = SqlWrapperFactory.getSqlWrapper();
        Connection con = null;

        try {
            // Connecting.
            con = sqlWrapper.doConnection();

            // Get next unique ID from database.
            id = sqlWrapper.getNextKey(con, tableName);

        } catch (SQLException sqlex) {
            ERROR(sqlex);
            throw new GenericSystemException(sqlex);

        } finally {
            sqlWrapper.closeConnection(con);
        }

        INFO("Got unigue message ID = " + id);
        return id;
    }
    
    /**
     * Sets specified Read flag for message using specified id.
     */
    public void setMessageReadFlag(long messageId, boolean isRead) {
        SqlWrapper sqlWrapper = SqlWrapperFactory.getSqlWrapper();
        String updateQuery = DBRealmManager.getSql("set_read_flag");
        Connection con = null;
        try {
            con = sqlWrapper.doConnection();
            PreparedStatement st = sqlWrapper.doPreparedStatement(con, updateQuery);
            sqlWrapper.getIntParser().setValue(st, 1, isRead ? 1 : 0);
            sqlWrapper.getLongParser().setValue(st, 2, messageId);
            sqlWrapper.executeUpdate(st);
        }
        catch(SQLException e) {
            ERROR(e);
            throw new GenericSystemException(e);
        }
        finally {
            sqlWrapper.closeConnection(con);
        }
    }
    
    public boolean hasUnreadMessages(LogonSession ls) {
        long userId = ls.getUser().getUserID();
        SqlWrapper sqlWrapper = SqlWrapperFactory.getSqlWrapper();
        String query = DBRealmManager.getSql("get_unread_messages_count");
        Connection con = null;
        try {
            con = sqlWrapper.doConnection();
            PreparedStatement st = sqlWrapper.doPreparedStatement(con, query);
            sqlWrapper.getLongParser().setValue(st, 1, userId);
            ResultSet rs = sqlWrapper.executeQuery(st);
            if (rs.next() && 
                sqlWrapper.getIntParser().getValue(rs, 1).intValue() != 0) {
                return true;
            } else {
                return false;
            }
        }
        catch(SQLException e) {
            ERROR(e);
            throw new GenericSystemException(e);
        }
        finally {
            sqlWrapper.closeConnection(con);
        }
    }

    /**
     * Saves the mail message into <b>Inbox</b> folder.
     *
     * @param ls      LogonSession
     * @param message InboxMessage
     * @return true
     */
    public boolean saveMailToInbox(LogonSession ls, InboxMessage message) {

        long time = System.currentTimeMillis();
        Exception error = null;

        message.setMessageType(Integer.valueOf(InboxHelper.EMAIL_MESSAGE));

        // save
        try {
            saveMail(ls, message);
        } catch (Exception ex) {
            error = ex;
        }

        // Logging.
        try {
            if(error != null) {
                String logMessage = "Can't save the message: " + error
                        .getMessage();
                InboxLogPublisher publisher = new InboxLogPublisher(ls);
                publisher.ERROR(logMessage, message);
                ERROR(logMessage, error);
                throw error;

            } else {
                String logMessage = "Message was saved to INBOX folder.";
                INFO(logMessage + " Time (ms) - "
                        + (System.currentTimeMillis() - time) + ". Message: "
                        + message);
                InboxLogPublisher publisher = new InboxLogPublisher(ls);
                publisher.INFO(logMessage, message);
            } // if (error != null)

        } catch (Exception ex) {
            // Re-throwing the exception.
            throwException("Known exception: " + ex.getMessage(), ex);
        } // try
        return true;
    } // saveMailToInbox(LogonSession, InboxMessage)

    /**
     * Saves the mail message into <b>Outbox</b> folder.
     *
     * @param ls      LogonSession
     * @param message InboxMessage
     */
    public void saveMailToOutbox(LogonSession ls, InboxMessage message) {
        // custom functionality
    } // saveMailToOutbox()

    /**
     * Saves the mail message into <b>Trash</b> folder.
     *
     * @param ls      LogonSession
     * @param message InboxMessage
     */
    public void saveMailToTrash(LogonSession ls, InboxMessage message) {

    } // saveMailToTrash()

    /**
     * Sends mail and copies the message data to the OUTBOX folder.
     *
     * @param ls           user logon session
     * @param inboxMessage mail message VO
     * @param mailHeaders  mail headers to add (<b>null</b> ignored)
     * @return true if the message was sent
     * @throws MessagingException if something wrong happened
     */
    public boolean sendEmailMessage(LogonSession ls, InboxMessage inboxMessage,
                                    Properties mailHeaders)
            throws MessagingException {

        // Initialization.
        long time = System.currentTimeMillis();
        MessagingException error = null;
        OutboxLogPublisher publisher = new OutboxLogPublisher(ls);

        // set image tag CID for the img resource
        // 1. Set image
        getAttchFromDBIntoMessage(ls, inboxMessage);
        
        getAttchFromUPServletIntoMessage(inboxMessage);

        if(!inboxMessage.isEmptyAttachmentsList()) {
            setNonTmpAttachIntoBody(inboxMessage);
        } // if (!inboxMessage.isEmptyAttachmentsList())

        // 2. Set 'Sent Date'.
        inboxMessage.setSentTime(DateHelper.getNowDate());

        // 3. Making special HEADER_X_TIKET header.
        Long objectId = inboxMessage.getObjectId();
        Integer objectType = inboxMessage.getObjectType();
        if(objectId != null) {
            if(mailHeaders == null) {
                mailHeaders = new Properties();
            } // if (mailHeaders == null)
            // Object ID and Object Type should be separated by ','
            // @see com.queplix.core.modules.inbox.utils.filters.InitialFilter
            String value = objectId.toString();
            if(objectType != null) {
                value += "," + objectType;
            }
            mailHeaders.setProperty(InboxHelper.HEADER_X_TIKET, value);
            DEBUG(InboxHelper.HEADER_X_TIKET + " header was set to: "
                    + objectId);
        } // if (objectId != null)

        // 4. Send message.
        MailManagerLocal local = (MailManagerLocal) getLocalObject(
                JNDINames.MailManager, MailManagerLocalHome.class);

        try {
            if(inboxMessage.isEmptyAttachmentsList()) {
                local.sendMessage(ls, inboxMessage, mailHeaders);
            } else {

                List msgAttachments = inboxMessage.getAttachments();
                Attachment[] attachments = new Attachment[inboxMessage
                        .getAttachments().size()];
                for(int i = 0; i < inboxMessage.getAttachments().size(); i++) {
                    Attachment attach = (Attachment) msgAttachments.get(i);
                    attachments[i] = attach;
                }
                local.sendMessage(ls, inboxMessage, mailHeaders, attachments);
                deleteTempAttachment(ls, inboxMessage);
            }
        } catch (MessagingException ex) {
            error = ex;
        } // try

        boolean returnValue;
        // Logging
        if(error != null) {
            String msg = "Cannot process the message: " + error.getMessage();
            publisher.ERROR(msg, inboxMessage);
            throw error;
        } else {
            String logMessage = "Message was sent and saved to OUTBOX folder.";
            INFO(logMessage + ". Time (ms) - "
                    + (System.currentTimeMillis() - time) + ". Message: "
                    + inboxMessage);

            publisher.INFO(logMessage, inboxMessage);

            returnValue = true;
        }
        return returnValue;
    } // sendEmailMessage(LogonSession, InboxMessage, Properties)

    /**
     * The method gets attachments from DB and saves their into the Inbox message object.
     *
     * @param ls           logon session object
     * @param inboxMessage message object
     * @return true
     */
    public boolean getAttchFromDBIntoMessage(LogonSession ls,
                                             InboxMessage inboxMessage) {

        INFO("Get attachments from DB and save into the Inbox Message object...");
        long time = System.currentTimeMillis();
        Exception error = null;
        RE re;
        boolean returnValue = false;

        try {
            re = new RE(InboxHelper.IMAGE_PATTERN_OUT_EMAIL_EXIST,
                    RE.MATCH_CASEINDEPENDENT);
            // Main cycle.
            int pos = 0;
            String newBody = inboxMessage.getBody();

            AttachmentTempObject[] dbAttachments = null;
            AttachmentManagerLocal attachMgr = getAttachmentManager();
            if(inboxMessage.getProcessId() != null) {
                dbAttachments = attachMgr.getTempAttachments(ls,
                        inboxMessage.getProcessId().longValue());
            }

            int counter = 0;
            // Quick fix for Ticket # 12965 
            if(newBody == null) newBody = "";
            // ---------------------------
            while(re.match(newBody, pos)) {

                // .. next start position
                pos = re.getParenEnd(0);
                String fullName = re.getParen(1);
                // get the process id
                long processID = Long.parseLong(re.getParen(2));
                // get the file name
                String fileName = re.getParen(3);

                // check is the attachment is already in the Temp table
                boolean isExist = false;
                if(dbAttachments != null) {
                    for(int t = 0; t < dbAttachments.length; t++) {
                        AttachmentTempObject obj = dbAttachments[t];
                        if(obj.getFilename().compareToIgnoreCase(fileName)
                                == 0) {
                            DEBUG("The attachment is already in the list...");
                            isExist = true;
                            break;
                        } // if (obj.getFilename().compareToIgnoreCase(fileName) == 0)
                    } // for(int t = 0; t < dbAttachments.length; t++ )
                } // if (dbAttachments != null)
                if(!isExist) {
                    // Get the attachment.
                    AttachmentObject obj;
                    JEOManagerLocal jeoManager = getJEOManager();
                    JEObjectHandler hnd = AttachmentObjectHandler
                            .selectByProcessFName(jeoManager, ls, processID,
                                    fileName);
                    if(hnd != null) {
                        obj = (AttachmentObject) hnd.getJEObject();
                    } else {
                        INFO("No attachments found for ProcessID = " + processID
                                + " and file name = " + fileName);
                        continue;
                    } // if (hnd != null)
                    String dbFileName = obj.getFilename();
                    byte[] dbFileData = obj.getData();
                    Attachment dbAttachment = new Attachment(dbFileName,
                            dbFileData);
                    inboxMessage.addAttachments(dbAttachment);
                } // if (isExist)

                int start = re.getParenStart(1);
                int end = re.getParenEnd(1);
                if(fileName.toLowerCase().startsWith(
                        InboxHelper.CONTENT_ID_CAPTION)) {
                    String normFileName = InboxHelper.normalizeAttachmentName(
                            fileName, counter);
                    newBody = newBody.substring(0, start) + InboxHelper
                            .CONTENT_ID_CAPTION + normFileName + newBody
                            .substring(end);
                    pos += (InboxHelper.CONTENT_ID_CAPTION + normFileName).length() - fullName.length();
                    counter++;
                } else {
                    newBody = newBody.substring(0, start) +
                            InboxHelper.CONTENT_ID_CAPTION + fileName + newBody
                            .substring(end);
                    // set pos
                    pos += (InboxHelper.CONTENT_ID_CAPTION + fileName).length() - fullName.length();
                } // if (fileName.toLowerCase().startsWith(InboxHelper.CONTENT_ID_CAPTION))
            } // while (re.match(body, pos))
            inboxMessage.setBody(newBody);

        } catch (Exception ex) {
            error = ex;
        } // try

        // Logging.
        try {
            if(error != null) {
                String logMessage =
                        "Can't get the attachments from DB for the sending email: "
                                + error.getMessage();
                InboxLogPublisher publisher = new InboxLogPublisher(ls);
                publisher.ERROR(logMessage, inboxMessage);
                ERROR(logMessage, error);
                throw error;
            } else {
                DEBUG("The attachments were got from the DB for the sending email. Time (ms) - "
                        + (System.currentTimeMillis() - time) +
                        ". Message: " + inboxMessage);
            } // if (error != null)

        } catch (Exception ex) {
            // Re-throwing the exception.
            throwException("Known exception: " + ex.getMessage(), ex);
        } // try

        INFO("The attachments were extracted from DB and saved in the Inbox Message object.");

        return returnValue;
    } // getAttchFromDBIntoMessage(LogonSession, InboxMessage) : boolean

    /**
     * The method inserts the CID tags for non tmp attachments into the email message body.
     *
     * @param inboxMessage message object
     * @return true
     */
    public boolean setNonTmpAttachIntoBody(InboxMessage inboxMessage) {

        return setContentIDAttachIntoBody(inboxMessage, InboxHelper.IMAGE_PATTERN_OUT_EMAIL);

    } // setNonTmpAttachIntoBody(LogonSession, InboxMessage) : boolean

    /**
     * The method uploads attachments using the file upload servlet.
     *
     * @param inboxMessage message object
     * @return true
     */
    public boolean getAttchFromUPServletIntoMessage(InboxMessage inboxMessage) {

        loadAttchFromUPServlet(inboxMessage);
        return setContentIDAttachIntoBody(inboxMessage, InboxHelper.IMAGE_PATTERN_UPSERVLET_OUT_EMAIL);

    } // getAttchFromUPServletIntoMessage(ls, inboxMessage) : boolean

    /**
     * The method uploads attachments using the file upload servlet.
     *
     * @param inboxMessage message object
     * @return true
     */
    private boolean loadAttchFromUPServlet(InboxMessage inboxMessage) {

        RE re;
        try {
            re = new RE(InboxHelper.IMAGE_PATTERN_UPSERVLET_OUT_EMAIL,
                    RE.MATCH_CASEINDEPENDENT);
        } catch (RESyntaxException ex) {
            ERROR(ex);
            throw new GenericSystemException(ex);
        } // try
        int pos = 0;
        String newBody = inboxMessage.getBody();
        List msgAttachments = null;
        if(!inboxMessage.isEmptyAttachmentsList())
            msgAttachments = inboxMessage.getAttachments();
        
        while(re.match(newBody, pos)) {

            // .. next start position
            pos = re.getParenEnd(0);

            // .. get image fullname (href) attribute
            String srcName = re.getParen(2);
            boolean isExist = false;
            // check if the attachment is present in the attachments list
            if (msgAttachments != null) {
                for(int i = 0; i < msgAttachments.size(); i++) {
                    Attachment attach = (Attachment) msgAttachments.get(i);
                    String fileName = attach.getFilename();
                    if(srcName.indexOf(fileName) > -1) {
                        isExist = true;
                        break;
                    } // if(srcName.indexOf(fileName) > -1)
                } // for(int i = 0; i < msgAttachments.size(); i++)
            } // if (msgAttachments != null)
            // if it is'n present in the list it shall be uploaded
            if (!isExist) {
                FileManager.FileInfo fi = InboxHelper.getAttachmentAsFile(srcName);
                File file = new File(fi.getFilePath());
                String fileName = file.getName();
                Attachment dbAttachment = new Attachment(fileName,
                        fi.getData());
                inboxMessage.addAttachments(dbAttachment);

            } // if (!isExist)
        } // while(re.match(newBody, pos))
        return true;
    } // getAttchFromUPServletIntoMessage(ls, inboxMessage) : boolean

    /**
     * The method inserts the CID tags for non tmp attachments into the email message body.
     * The attachments attached to the Inbox Message object.
     *
     * @param inboxMessage message object
     * @return true
     */
    private boolean setContentIDAttachIntoBody(InboxMessage inboxMessage, String paternString) {

        INFO("Set CID tag into the email body for the attachments...");

        List msgAttachments = null;
        if(!inboxMessage.isEmptyAttachmentsList())
            msgAttachments = inboxMessage.getAttachments();
        if(msgAttachments != null && msgAttachments.size() > 0) {
            RE re;
            try {
                re = new RE(paternString,
                        RE.MATCH_CASEINDEPENDENT);
            } catch (RESyntaxException ex) {
                ERROR(ex);
                throw new GenericSystemException(ex);
            } // try
            // Main cycle.
            int pos = 0;
            String newBody = inboxMessage.getBody();
            while(re.match(newBody, pos)) {

                // .. next start position
                pos = re.getParenEnd(0);

                // .. get image fullname (href) attribute
                String srcName = re.getParen(1);

                // Try to find the same attachment among temp attachments.
                // Process the attachments list.
                srcName = InboxHelper.getContentId(srcName);
                for(int i = 0; i < msgAttachments.size(); i++) {
                    Attachment attach = (Attachment) msgAttachments.get(i);
                    String fileName = attach.getFilename();
                    if(srcName.indexOf(fileName) > -1) {
                        int start = re.getParenStart(1);
                        int end = re.getParenEnd(1);
                        if(fileName.toLowerCase().startsWith(
                                InboxHelper.CONTENT_ID_CAPTION)) {
                            String normFileName = InboxHelper
                                    .normalizeAttachmentName(fileName, i);
                            newBody = newBody.substring(0, start) + InboxHelper
                                    .CONTENT_ID_CAPTION + normFileName + newBody
                                    .substring(end);
                        } else {
                            newBody = newBody.substring(0, start) +
                                    InboxHelper.CONTENT_ID_CAPTION + fileName
                                    + newBody.substring(end);
                        } // if (fileName.toLowerCase().startsWith(InboxHelper.CONTENT_ID_CAPTION))
                        break;
                    } // if (fileName.indexOf(srcName) > -1)
                } // for (Iterator it = bodyAttachments.iterator(); it.hasNext();)
            } // while (re.match(body, pos))
            inboxMessage.setBody(newBody);
        } // if (inboxMessage.getProcessId() != null)

        INFO("The CID tag was set in the email body for the attachments.");

        return true;
    } // boolean setNonTmpAttachIntoBody(LogonSession, InboxMessage)

    /**
     * The method saves an alert message.
     *
     * @param ls           logon session object
     * @param inboxMessage message object
     * @return true
     */
    public boolean sendAlertMessage(LogonSession ls,
                                    InboxMessage inboxMessage) {

        INFO("Create non Email Message");
        long time = System.currentTimeMillis();
        Exception error = null;

        // Now date.
        Date now = DateHelper.getNowDate();

        inboxMessage.setMessageType(Integer.valueOf(InboxHelper.ALERT_MESSAGE));
        inboxMessage.setReceiveTime(now);
        inboxMessage.setSentTime(now);
        // Save!
        try {
            saveMail(ls, inboxMessage);
        } catch (Exception ex) {
            error = ex;
        } // try

        // Logging.
        try {
            if(error != null) {
                String logMessage = "Can't save the message: " + error
                        .getMessage();
                InboxLogPublisher publisher = new InboxLogPublisher(ls);
                publisher.ERROR(logMessage, inboxMessage);
                ERROR(logMessage, error);
                throw error;
            } else {
                String logMessage = "Message was saved to INBOX folder.";
                INFO(logMessage + " Time (ms) - "
                        + (System.currentTimeMillis() - time) +
                        ". Message: " + inboxMessage);

                InboxLogPublisher publisher = new InboxLogPublisher(ls);
                publisher.INFO(logMessage, inboxMessage);
            }

        } catch (Exception ex) {
            // Re-throwing the exception.
            throwException("Known exception: " + ex.getMessage(), ex);
        } // try

        INFO("Non Email Message was saved");
        return true;
    } // sendAlertMessage(LogonSession, InboxMessage) : boolean

    /**
     * The method sets the image loader for the inner images.
     *
     * @param ls           logon session object
     * @param inboxMessage message object
     * @return true
     */
    public boolean setImageAttachmentLoader(LogonSession ls,
                                            InboxMessage inboxMessage) {

        INFO("Set image loader into the email message body");
        long time = System.currentTimeMillis();
        Exception error = null;

        // check if the attachements have alrready saved
        if(!inboxMessage.isAttachmentSaved()) {
            saveAttachment(ls, inboxMessage);
        } // if (!inboxMessage.isAttachmentSaved())

        Set attachments = inboxMessage.getAttachmentIDsList().keySet();

        // RE to find '<img' inside HTML body
        RE re;
        try {
            re = new RE(InboxHelper.IMAGE_PATTERN_INCOM,
                    RE.MATCH_CASEINDEPENDENT);
            // Main cycle.
            int pos = 0;
            String newBody = inboxMessage.getBody();
            while(re.match(newBody, pos)) {

                // .. next start position
                pos = re.getParenEnd(0);

                // .. get image fullname (href) attribute
                String srcName = re.getParen(1);

                srcName = InboxHelper.getContentId(srcName);
                // Try to find the same attachment among attachments.
                // Process the attachments list.
                for(Iterator it = attachments.iterator(); it.hasNext();) {
                    Long attachId = (Long) it.next();
                    String fileName = (String) inboxMessage
                            .getAttachmentIDsList().get(attachId);
                    if(fileName.indexOf(srcName) > -1) {
                        // set loader servlet
                        int start = re.getParenStart(1);
                        int end = re.getParenEnd(1);
                        newBody = newBody.substring(0, start) +
                                InboxHelper.SERVLET_UPLOAD_NAME
                                + inboxMessage.getProcessId() +
                                InboxHelper.SERVLET_UPLOAD_FILENAME + fileName
                                + newBody.substring(end);
                        break;
                    } // if (fileName.indexOf(srcName) > -1)
                } // for (Iterator it = bodyAttachments.iterator(); it.hasNext();)
            } // while (re.match(body, pos))
            inboxMessage.setBody(newBody);
        } catch (RESyntaxException ex) {
            error = ex;
        } // try

        // Logging.
        try {
            if(error != null) {
                String logMessage =
                        "Can't set image loader into the email message: "
                                + error.getMessage();
                InboxLogPublisher publisher = new InboxLogPublisher(ls);
                publisher.ERROR(logMessage, inboxMessage);
                ERROR(logMessage, error);
                throw error;
            } else {
                DEBUG("The image loader was set into the email message body. Time (ms) - "
                        +
                        (System.currentTimeMillis() - time) + ". Message: "
                        + inboxMessage);
            } // if (error != null)
        } catch (Exception ex) {
            // Re-throwing the exception.
            throwException("Known exception: " + ex.getMessage(), ex);
        } // try

        INFO("The image loader was set into the email message body.");

        return true;
    } // setImageAttachmentLoader(LogonSession, InboxMessage) : boolean

    /**
     * The method sets the image loader for the inner images.
     *
     * @param ls           logon session object
     * @param inboxMessage message object
     * @return true
     */
    public boolean changeReferenceHTMLATAG(LogonSession ls,
                                           InboxMessage inboxMessage) {

        INFO("Change <a> html tag ...");

        // RE to find '<img' inside HTML body
        RE re;
        try {
            re = new RE(InboxHelper.TAG_A_PATTERN, RE.MATCH_CASEINDEPENDENT);
        } catch (RESyntaxException ex) {
            ERROR(ex);
            throw new GenericSystemException(ex);
        } // try
        // Main cycle.
        int pos = 0;
        String newBody = inboxMessage.getBody();
        while(re.match(newBody, pos)) {
            // .. next start position
            pos = re.getParenEnd(0);

            int end = re.getParenEnd(1);
            newBody = newBody.substring(0, end + 1) + InboxHelper.TAG_A_TARGET
                    + newBody.substring(end + 1);
        } // while (re.match(body, pos))
        inboxMessage.setBody(newBody);

        return true;
    } // changeReferenceHTMLATAG(LogonSession, InboxMessage) : boolean

    //
    // Gets the JEO Manager EJB local interface.
    //
    public JEOManagerLocal getJEOManager() {
        return (JEOManagerLocal) getLocalObject(JNDINames.JEOManager,
                JEOManagerLocalHome.class);
    } // getJEOManager() : JEOManagerLocal

    /**
     * The method deletes temp attachments from the DB.
     *
     * @param ls           logon session object
     * @param inboxMessage message object
     */
    public void deleteTempAttachment(LogonSession ls,
                                     InboxMessage inboxMessage) {
        INFO("Delete temp attachments...");

        if(inboxMessage.getProcessId() != null) {
            long processId = inboxMessage.getProcessId().longValue();
            AttachmentManagerLocal attachMgr = getAttachmentManager();
            // Clear temp attachments.
            attachMgr.deleteTempAttachments(ls, processId);
        }
        INFO("Temp attachments were deleted...");
    } // deleteTempAttachment(LogonSession, InboxMessage)

    /**
     * The method saves attachments to the DB.
     *
     * @param ls           logon session object
     * @param inboxMessage message object
     * @return true
     */
    public boolean saveAttachment(LogonSession ls, InboxMessage inboxMessage) {

        INFO("Save message attachments...");

        boolean returnValue;
        if(inboxMessage.getProcessId() != null) {
            if(!inboxMessage.isAttachmentSaved()) {
                long processId = inboxMessage.getProcessId().longValue();
                AttachmentManagerLocal attachMgr = getAttachmentManager();

                // Create  normal attachments from temp.
                HashMap attachmentsId = attachMgr.addAttachments(ls, processId);
                inboxMessage.setAttachmentIDsList(attachmentsId);

                returnValue = true;
                INFO("The attachments were saved.");
            } else {
                returnValue = true;
                INFO("The attachments have already saved.");
            } // if (!inboxMessage.isAttachmentSaved())
            inboxMessage.setIsAttachmentSaved(true);
        } else {
            returnValue = true;
        } // if (message.getProcessId() != null)

        return returnValue;
    } // saveAttachment(LogonSession, InboxMessage) : boolean

    /**
     * The method links attachments to the object.
     *
     * @param ls           logon session object
     * @param inboxMessage message object
     * @return true
     */
    public boolean linkAttachmentsToObject(LogonSession ls,
                                           InboxMessage inboxMessage) {
        return linkAttachmentsToObject(ls, inboxMessage, false, null);
    } // linkAttachmentsToObject(LogonSession, InboxMessage) : boolean

    /**
     * The method links attachments to the object.
     *
     * @param ls           logon session object
     * @param inboxMessage message object
     * @return true
     */
    public boolean setReplyDateToInbox(LogonSession ls,
                                       InboxMessage inboxMessage) {

        INFO("Update Inbox Record, set the Replied Date field ...");

        // try to find the lastest message by the object
        JEOManagerLocal jeoManager = getJEOManager();
        long time = System.currentTimeMillis();
        Exception error = null;
        boolean returnValue = false;
        try {
            List inboxHandList = InboxObjectHandler.selectByLinkObj(jeoManager,
                    ls, inboxMessage.getObjectId().longValue(),
                    inboxMessage.getObjectType());
            // check if the message exist
            if(inboxHandList == null) {
                returnValue = true;
            } else {
                // get the latest record
                InboxObjectHandler inboxHndRecord
                        = (InboxObjectHandler) inboxHandList.get(0);
                InboxObject inboxObject = (InboxObject) inboxHndRecord
                        .getJEObject();
                inboxObject.setReplied_date(DateHelper.getNowDate());
                inboxHndRecord.commit();
            }
        } catch (Exception ex) {
            error = ex;
        } // try

        // Logging.
        try {
            if(error != null) {
                String logMessage = "The Inbox Record wasn't updated: " + error
                        .getMessage();
                InboxLogPublisher publisher = new InboxLogPublisher(ls);
                publisher.ERROR(logMessage, inboxMessage);
                ERROR(logMessage, error);
                throw error;
            } else {
                String logMessage =
                        "The Inbox Record was updated. The object ID is " +
                                inboxMessage.getObjectId().longValue() +
                                " The object type is " + inboxMessage
                                .getObjectType();
                INFO(logMessage + " Time (ms) - "
                        + (System.currentTimeMillis() - time) +
                        ". Message: " + inboxMessage);

                InboxLogPublisher publisher = new InboxLogPublisher(ls);
                publisher.INFO(logMessage, inboxMessage);
                returnValue = true;
            } // if (error != null)

        } catch (Exception ex) {
            // Re-throwing the exception.
            throwException("Known exception: " + ex.getMessage(), ex);
        } // try

        INFO("Update Inbox Record, the Replied Date field was set ...");

        return returnValue;
    } // setReplyDateToInbox(LogonSession, InboxMessage) : boolean

    // ------------------------------------------------------- Private methods

    //
    // Saves the mail message in the mail store database table.
    //

    private void saveMail(LogonSession ls, InboxMessage inboxMessage) {

        // Initialization.
        JEOManagerLocal jeoManager = getJEOManager();

        // Go!
        try {
            // 1. Saving message.
            // ==================

            JEObjectHandler hnd = jeoManager.create(ls,
                    InboxObjectHandler.class);
            InboxObject obj = (InboxObject) hnd.getJEObject();

            // Message ID.
            Long messageID = inboxMessage.getMessageId();
            if(messageID == null) {
                // Generate new message ID.
                messageID = new Long(getUniqueMessageID());

                // Update message object.
                inboxMessage.setMessageId(messageID);
            } // if (messageID == null)

            // 'From'.
            if(inboxMessage.getResipient() == null
                    && inboxMessage.getWorkgroupId() == null) {
                throw new NullPointerException(
                        "The message wasn't saved. The Recipient or Worgroup field should be filled in.");
            } // if (message.getFrom() == null)

            // Sender ID.
            obj.setMessage_sender(inboxMessage.getSender());
            // Owner ID.
            obj.setRecipient_id(inboxMessage.getResipient());
            // Workgroup ID.
            obj.setWorkgroup_id(inboxMessage.getWorkgroupId());

            // Linked object.
            obj.setObject_id(inboxMessage.getObjectId());
            obj.setObject_type(inboxMessage.getObjectType());

            if(inboxMessage.getFrom() != null) {
                obj.setMessage_sender_email(
                        inboxMessage.getFrom().toRfcString());
            }

            if(inboxMessage.getTo() != null) {
                obj.setEmail_to(MailAddress.toRfcString(inboxMessage.getTo()));
            } // if (message.getTo() != null)

            // 'Cc'.
            if(inboxMessage.getCc() != null) {
                obj.setEmail_cc(MailAddress.toRfcString(inboxMessage.getCc()));
            } // if (message.getCc() != null)

            // Mail subject.
            obj.setSubject(inboxMessage.getSubject());

            // Mail body.
            String body = inboxMessage.getBody();

            if(body != null) {
                obj.setMessage(body.toCharArray());

                if(inboxMessage.getMessageDigest() != null
                        && inboxMessage.getMessageDigest().length() > 0) {
                    obj.setDigest_src(inboxMessage.getMessageDigest());
                }
            } // if (body != null)

            // Process ID..
            if(inboxMessage.getProcessId() != null) {
                obj.setProcess_id(inboxMessage.getProcessId());
            }

            obj.setMessage_type(inboxMessage.getMessageType());

            // Sent Date.
            obj.setSent_date(inboxMessage.getSentTime());

            // Received Date.
            if(inboxMessage.getMessageType().compareTo(Integer.valueOf(
                    InboxHelper.EMAIL_MESSAGE)) == 0) {
                obj.setServer_received_date(inboxMessage.getReceiveTime());
                obj.setReceived_date(DateHelper.getNowDate());
            } else {
                if(inboxMessage.getReceiveTime() == null) {
                    obj.setReceived_date(DateHelper.getNowDate());
                } else {
                    obj.setReceived_date(inboxMessage.getReceiveTime());
                }
            }

            // Account ID.
            if(inboxMessage.getAccountId() != null) {
                obj.setAccount_id(inboxMessage.getAccountId());
            }

            // Adding the message record.
            hnd.commit();

            // 2. Handling attachments.
            // ========================

            if(inboxMessage.getProcessId() != null) {
                linkAttachmentsToObject(ls, inboxMessage, true, obj.getPkey());
            }

        } catch (EQLException ex) {
            throwException(ex);
        } // try
    } // saveMail(LogonSession, InboxMessage)

    /**
     * The method links attachments to the object.
     *
     * @param ls             logon session object
     * @param inboxMessage   message object
     * @param isInboxMessage save link for the inbox message
     * @param inboxId        record inbox indetificator
     * @return true
     */
    private boolean linkAttachmentsToObject(LogonSession ls,
                                            InboxMessage inboxMessage,
                                            boolean isInboxMessage,
                                            Long inboxId) {

        INFO("Link attachments to the object...");
        boolean returnValue = false;

        // check if the attachements have alrready saved
        if(!inboxMessage.isAttachmentSaved()) {
            saveAttachment(ls, inboxMessage);
        } // if (!inboxMessage.isAttachmentSaved())

        // Initialization.
        JEOManagerLocal jeoManager = getJEOManager();

        Integer objectType;
        Long objectId;
        if(isInboxMessage) {
            objectType = inboxMessage.getMessageObjectType();
            objectId = inboxId;
        } else {
            objectType = inboxMessage.getObjectType();
            objectId = inboxMessage.getObjectId();
        }
        // create attachement links to the object
        try {
            Set attachmentIDsList = inboxMessage.getAttachmentIDsList()
                    .keySet();
            if(attachmentIDsList != null) {
                for(Iterator it = attachmentIDsList.iterator(); it.hasNext();) {
                    Long attachId = (Long) it.next();
                    JEObjectHandler hnd = jeoManager.create(ls,
                            AttachmentObjectsObjectHandler.class);
                    AttachmentObjectsObject attachObj
                            = (AttachmentObjectsObject) hnd.getJEObject();
                    attachObj.setAttachment_id(attachId);
                    attachObj.setObject_id(objectId);
                    attachObj.setObject_type(objectType.intValue());
                    hnd.commit();
                } // for (Iterator it = attachmentIDsList.iterator(); it.hasNext();)
            } else {
                INFO("Link attachments wasn't set. The Attachments list wasn't found.");
            } // if (attachmentIDsList != null)
        } catch (EQLException ex) {
            throwException(ex);
        } // try

        INFO("Link attachments to the object were created...");

        return returnValue;
    } // linkAttachmentsToObject(LogonSession, InboxMessage, boolean) : boolean

    //
    // Gets Attachment Manager EJB reference.
    //
    private AttachmentManagerLocal getAttachmentManager() {
        return (AttachmentManagerLocal) getLocalObject(
                JNDINames.AttachmentManager, AttachmentManagerLocalHome.class);
    }
} // class InboxManagerEJB

