com.sonicle.webtop.mail.Service.java Source code

Java tutorial

Introduction

Here is the source code for com.sonicle.webtop.mail.Service.java

Source

/*
 * webtop-mail is a WebTop Service developed by Sonicle S.r.l.
 * Copyright (C) 2014 Sonicle S.r.l.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY SONICLE, SONICLE DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Sonicle S.r.l. at email address sonicle@sonicle.com
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Sonicle WebTop" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Sonicle WebTop".
 */
package com.sonicle.webtop.mail;

import com.sonicle.commons.AlgoUtils;
import com.sonicle.commons.EnumUtils;
import com.sonicle.commons.InternetAddressUtils;
import com.sonicle.webtop.core.app.PrivateEnvironment;
import com.sonicle.webtop.core.CoreLocaleKey;
import com.sonicle.commons.LangUtils;
import java.nio.*;
import java.nio.channels.*;
import com.sonicle.commons.MailUtils;
import com.sonicle.commons.PathUtils;
import com.sonicle.commons.RegexUtils;
import com.sonicle.commons.URIUtils;
import com.sonicle.commons.db.DbUtils;
import com.sonicle.commons.http.HttpClientUtils;
import com.sonicle.commons.time.DateTimeUtils;
import com.sonicle.commons.web.Crud;
import com.sonicle.commons.web.DispositionType;
import com.sonicle.commons.web.ServletUtils;
import com.sonicle.commons.web.json.CompositeId;
import com.sonicle.commons.web.json.JsonResult;
import com.sonicle.commons.web.json.MapItem;
import com.sonicle.commons.web.json.MapItemList;
import com.sonicle.commons.web.json.Payload;
import com.sonicle.mail.imap.SonicleIMAPFolder;
import com.sonicle.mail.imap.SonicleIMAPMessage;
import com.sonicle.mail.sieve.SieveAction;
import com.sonicle.mail.sieve.SieveActionMethod;
import com.sonicle.security.AuthenticationDomain;
import com.sonicle.security.Principal;
import com.sonicle.security.auth.directory.LdapNethDirectory;
import com.sonicle.webtop.calendar.model.GetEventScope;
import com.sonicle.webtop.calendar.ICalendarManager;
import com.sonicle.webtop.calendar.model.Event;
import com.sonicle.webtop.contacts.IContactsManager;
import com.sonicle.webtop.contacts.io.ContactInput;
import com.sonicle.webtop.contacts.io.VCardInput;
import com.sonicle.webtop.contacts.model.ContactPictureWithBytes;
import com.sonicle.webtop.contacts.model.ContactQuery;
import com.sonicle.webtop.core.CoreManager;
import com.sonicle.webtop.core.CoreUserSettings;
import com.sonicle.webtop.core.app.DocEditorManager;
import com.sonicle.webtop.core.app.RunContext;
import com.sonicle.webtop.core.app.WT;
import com.sonicle.webtop.core.app.WebTopSession;
import com.sonicle.webtop.core.app.WebTopSession.UploadedFile;
import com.sonicle.webtop.core.app.sdk.BaseDocEditorDocumentHandler;
import com.sonicle.webtop.core.app.servlet.ResourceRequest;
import com.sonicle.webtop.core.bol.OUser;
import com.sonicle.webtop.core.bol.js.JsHiddenFolder;
import com.sonicle.webtop.core.bol.js.JsSimple;
import com.sonicle.webtop.core.model.Recipient;
import com.sonicle.webtop.core.model.SharePermsElements;
import com.sonicle.webtop.core.model.SharePermsFolder;
import com.sonicle.webtop.core.bol.model.Sharing;
import com.sonicle.webtop.core.dal.UserDAO;
import com.sonicle.webtop.core.sdk.*;
import com.sonicle.webtop.core.sdk.interfaces.IServiceUploadStreamListener;
import com.sonicle.webtop.core.app.servlet.ServletHelper;
import com.sonicle.webtop.core.util.ICalendarUtils;
import com.sonicle.webtop.mail.bol.ONote;
import com.sonicle.webtop.mail.bol.OScan;
import com.sonicle.webtop.mail.bol.OUserMap;
import com.sonicle.webtop.mail.bol.js.JsAttachment;
import com.sonicle.webtop.mail.bol.js.JsContactData;
import com.sonicle.webtop.mail.bol.js.JsFolder;
import com.sonicle.webtop.mail.bol.js.JsInMailFilters;
import com.sonicle.webtop.mail.bol.js.JsMailAutosave;
import com.sonicle.webtop.mail.bol.js.JsMessage;
import com.sonicle.webtop.mail.bol.js.JsPortletSearchResult;
import com.sonicle.webtop.mail.bol.js.JsPreviewMessage;
import com.sonicle.webtop.mail.bol.js.JsQuickPart;
import com.sonicle.webtop.mail.bol.js.JsRecipient;
import com.sonicle.webtop.mail.bol.js.JsSharing;
import com.sonicle.webtop.mail.bol.js.JsSmartSearchTotals;
import com.sonicle.webtop.mail.bol.js.JsSort;
import com.sonicle.webtop.mail.bol.js.JsTag;
import com.sonicle.webtop.mail.bol.model.Identity;
import com.sonicle.webtop.mail.dal.NoteDAO;
import com.sonicle.webtop.mail.dal.ScanDAO;
import com.sonicle.webtop.mail.dal.UserMapDAO;
import com.sonicle.webtop.mail.model.AutoResponder;
import com.sonicle.webtop.mail.model.ExternalAccount;
import com.sonicle.webtop.mail.model.MailEditFormat;
import com.sonicle.webtop.mail.model.MailFilter;
import com.sonicle.webtop.mail.model.MailFiltersType;
import com.sonicle.webtop.mail.model.Tag;
import com.sonicle.webtop.mail.ws.AddContactMessage;
import com.sonicle.webtop.vfs.IVfsManager;
// TODO: Fix imported classes
//import com.sonicle.webtop.Mailcard;
//import com.sonicle.webtop.EditingSession;
//import com.sonicle.webtop.bol.OServiceSetting;
//import com.sonicle.webtop.bol.OUser;
//import com.sonicle.webtop.bol.OWorkgroup;
//import com.sonicle.webtop.bol.WebTopSettings;
//import com.sonicle.webtop.bol.js.JsSharingRight;
//import com.sonicle.webtop.contacts.*;
//import com.sonicle.webtop.dal.WebTopDb;
//import com.sonicle.webtop.mail.bol.JsMailcard;
//import com.sonicle.webtop.mail.bol.JsQuickPart;
//import com.sonicle.webtop.mail.dal.InboxMailFiltersDb;
//import com.sonicle.webtop.mail.dal.SentMailFiltersDb;
//import com.sonicle.webtop.profiledata.ProfileDataProviderBase;
//import com.sonicle.webtop.profiledata.ProfilePersonalInfo;
//import com.sonicle.webtop.setting.SettingsManager;
//import com.sonicle.webtop.util.*;
//import com.sonicle.webtop.vfs.VFSService;
import com.sun.mail.imap.*;
import com.sun.mail.util.PropUtil;
import java.io.*;
import java.net.URI;
import java.sql.*;
import java.text.DateFormat;
import java.text.MessageFormat;
import java.util.*;
import java.util.jar.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.activation.*;
import javax.mail.*;
import javax.mail.Message.RecipientType;
import javax.mail.internet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.fortuna.ical4j.model.parameter.PartStat;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.vfs2.FileObject;
import org.apache.http.client.HttpClient;
import org.apache.commons.vfs2.FileSystemException;
import org.joda.time.DateTimeZone;
import com.github.rutledgepaulv.qbuilders.conditions.Condition;
import com.sonicle.commons.web.ParameterException;
import com.sonicle.commons.web.json.bean.QueryObj;
import com.sonicle.webtop.mail.bol.model.ImapQuery;
import javax.mail.search.AndTerm;
import javax.mail.search.OrTerm;
import javax.mail.search.SearchTerm;
import org.slf4j.Logger;

public class Service extends BaseService {

    public final static Logger logger = WT.getLogger(Service.class);

    class WebtopFlag {
        String label;
        //String tbLabel;

        WebtopFlag(String label/*, String tbLabel*/) {
            this.label = label;
            //this.tbLabel=tbLabel;
        }

    }

    public WebtopFlag[] webtopFlags = { new WebtopFlag("red"/*,"$label1"*/), new WebtopFlag("blue"/*,"$label4"*/),
            new WebtopFlag("yellow"/*,null*/), new WebtopFlag("green"/*,"$label3"*/),
            new WebtopFlag("orange"/*,"$label2"*/), new WebtopFlag("purple"/*,"$label5"*/),
            new WebtopFlag("black"/*,null*/), new WebtopFlag("gray"/*,null*/), new WebtopFlag("white"/*,null*/),
            new WebtopFlag("brown"/*,null*/), new WebtopFlag("azure"/*,null*/), new WebtopFlag("pink"/*,null*/),
            new WebtopFlag("complete"/*,null*/) };

    public String allFlagStrings[];

    /*class ThunderbirdFlag {
       String label;
       int colorindex;
           
       ThunderbirdFlag(String label, int colorindex) {
     this.label=label;
     this.colorindex=colorindex;
       }
    }*/

    /*    public ThunderbirdFlag tbFlagStrings[]={
    new ThunderbirdFlag("$label1",0),   //red
    new ThunderbirdFlag("$label2",4),   //orange
    new ThunderbirdFlag("$label3",3),   //green
    new ThunderbirdFlag("$label4",1),   //blue
    new ThunderbirdFlag("$label5",5)   //purple
              
        };*/

    public static Flags flagsAll = new Flags();
    public static Flags oldFlagsAll = new Flags();
    //public static Flags tbFlagsAll=new Flags();
    public static HashMap<String, Flags> flagsHash = new HashMap<String, Flags>();
    public static HashMap<String, Flags> oldFlagsHash = new HashMap<String, Flags>();
    //public static HashMap<String,Flags> tbFlagsHash=new HashMap<String,Flags>();
    private static String sflagNote = "mailnote";
    private static String sflagDmsArchived = "$Archived";
    public static Flags flagNote = new Flags(sflagNote);
    public static Flags flagDmsArchived = new Flags(sflagDmsArchived);
    public static Flags flagFlagged = new Flags(Flags.Flag.FLAGGED);

    protected List<Tag> atags = new ArrayList<>();
    protected HashMap<String, Tag> htags = new HashMap<>();

    private FetchProfile FP = new FetchProfile();
    private FetchProfile draftsFP = new FetchProfile();
    private FetchProfile pecFP = new FetchProfile();

    public static final String HDR_PEC_PROTOCOLLO = "X-Protocollo";
    public static final String HDR_PEC_RIFERIMENTO_MESSAGE_ID = "X-Riferimento-Message-ID";
    public static final String HDR_PEC_TRASPORTO = "X-Trasporto";
    public static final String HDR_PEC_RICEVUTA = "X-Ricevuta";
    public static final String HDR_PEC_TIPORICEVUTA = "X-Tiporicevuta";

    public static final String HDR_PEC_RICEVUTA_VALUE_ACCETTAZIONE = "accettazione";
    public static final String HDR_PEC_RICEVUTA_VALUE_NON_ACCETTAZIONE = "non-accettazione";
    public static final String HDR_PEC_RICEVUTA_VALUE_AVVENUTA_CONSEGNA = "avvenuta-consegna";
    public static final String HDR_PEC_TIPORICEVUTA_VALUE_BREVE = "breve";
    public static final String HDR_PEC_TIPORICEVUTA_VALUE_COMPLETA = "completa";

    private boolean sortfolders = false;

    //private boolean hasDifferentDefaultFolder=false;

    public static final String HEADER_SONICLE_FROM_DRAFTER = "Sonicle-from-drafter";

    public static final String HEADER_X_WEBTOP_MSGID = "X-WEBTOP-MSGID";

    static String startpre = "<PRE>";
    static String endpre = "</PRE>";
    //   private static String webtopTextMessage
    //         = "This email has been sent through Sonicle WebMail system [ http://www.sonicle.com ]";
    //   private static String webtopHTMLMessage = "This email has been sent through Sonicle WebMail system [ <A HREF='http://www.sonicle.com'>http://www.sonicle.com</A> ]";
    //   private static String unwantedTags[] = {"style"};

    protected static final String MAIN_ACCOUNT_ID = "main";
    protected static final String ARCHIVE_ACCOUNT_ID = "archive";

    private MailManager mailManager;
    private MailAccount mainAccount = null;
    private MailAccount archiveAccount = null;
    private ArrayList<MailAccount> externalAccounts = new ArrayList<MailAccount>();
    private HashMap<String, MailAccount> accounts = new HashMap<>();
    private HashMap<String, ExternalAccount> externalAccountsMap = new HashMap<>();
    //private Session session;
    //private Store store;
    //private String storeProtocol;
    //private boolean disconnecting = false;
    //private String sharedPrefixes[] = null;
    //private char folderSeparator = 0;
    //private String folderPrefix = null;

    private PrivateEnvironment environment = null;
    private MailUserProfile mprofile;
    private MailServiceSettings ss = null;
    private MailUserSettings us = null;
    private CoreUserSettings cus = null;
    //private boolean validated = false;
    private int newMessageID = 0;
    private MailFoldersThread mft;
    //private boolean hasAnnotations=false;

    //private HashMap<String, FolderCache> foldersCache = new HashMap<String, FolderCache>();
    //private FolderCache fcRoot = null;
    //private FolderCache[] fcShared = null;
    private FolderCache fcProvided = null;

    //private String skipReplyFolders[] = null;
    //private String skipForwardFolders[] = null;

    private static ArrayList<String> inlineableMimes = new ArrayList<String>();

    private HashMap<Long, ArrayList<CloudAttachment>> msgcloudattach = new HashMap<Long, ArrayList<CloudAttachment>>();
    private ArrayList<CloudAttachment> emptyAttachments = new ArrayList<CloudAttachment>();

    private AdvancedSearchThread ast = null;

    private IVfsManager vfsmanager = null;
    private SmartSearchThread sst;
    private PortletSearchThread pst;

    private boolean previewBalanceTags = true;

    static {
        inlineableMimes.add("image/gif");
        inlineableMimes.add("image/jpeg");
        inlineableMimes.add("image/png");
        //      inlineableMimes.add("image/tiff");
        //      inlineableMimes.add("text/plain");
        //      inlineableMimes.add("message/rfc822");
    }

    @Override
    public void initialize() {

        ArrayList<String> allFlagsArray = new ArrayList<String>();
        for (WebtopFlag fs : webtopFlags) {
            allFlagsArray.add(fs.label);
            String oldfs = "flag" + fs.label;
            flagsAll.add(fs.label);
            //if (fs.tbLabel!=null) tbFlagsAll.add(fs.tbLabel);
            oldFlagsAll.add(oldfs);
            Flags flags = new Flags();
            flags.add(fs.label);
            flagsHash.put(fs.label, flags);
            flags = new Flags();
            flags.add(oldfs);
            oldFlagsHash.put(fs.label, flags);
            /*if (fs.tbLabel!=null) {
               Flags tbFlags=new Flags();
               tbFlags.add(fs.tbLabel);
               tbFlagsHash.put(fs.label, tbFlags);
            }*/
        }
        /*for(WebtopFlag fs: webtopFlags) {
           if (fs.tbLabel!=null) allFlagsArray.add(fs.tbLabel);
        }*/
        for (WebtopFlag fs : webtopFlags) {
            allFlagsArray.add("flag" + fs.label);
        }
        allFlagStrings = new String[allFlagsArray.size()];
        allFlagsArray.toArray(allFlagStrings);

        this.environment = getEnv();

        mailManager = (MailManager) WT.getServiceManager(SERVICE_ID);
        cus = new CoreUserSettings(getEnv().getProfileId());

        UserProfile profile = getEnv().getProfile();
        ss = new MailServiceSettings(SERVICE_ID, getEnv().getProfile().getDomainId());
        us = new MailUserSettings(profile.getId(), ss);
        //mprofile = new MailUserProfile(environment,this);
        mprofile = new MailUserProfile(mailManager, ss, us, profile);
        String mailUsername = mprofile.getMailUsername();
        String mailPassword = mprofile.getMailPassword();
        String authorizationId = mailUsername;
        boolean isImpersonated = profile.getPrincipal().isImpersonated();
        String vmailSecret = ss.getNethTopVmailSecret();
        if (isImpersonated) {
            //use sasl rfc impersonate if no vmailSecret
            if (vmailSecret == null) {
                //TODO: implement sasl rfc authorization id if possible
                //session.getProperties().setProperty("mail.imap.sasl.authorizationid", authorizationId);
                //mailUsername=ss.getAdminUser();
                //mailPassword=ss.getAdminPassword();
            } else {
                mailUsername += "*vmail";
                mailPassword = vmailSecret;
            }
        }
        mailManager.setSieveConfiguration(mprofile.getMailHost(), ss.getSievePort(), mailUsername, mailPassword);
        fcProvided = new FolderCache(this, environment);

        previewBalanceTags = ss.isPreviewBalanceTags();

        mainAccount = createAccount(MAIN_ACCOUNT_ID);
        mainAccount.setFolderPrefix(mprofile.getFolderPrefix());
        mainAccount.setProtocol(mprofile.getMailProtocol());

        FP.add(FetchProfile.Item.ENVELOPE);
        FP.add(FetchProfile.Item.FLAGS);
        FP.add(FetchProfile.Item.CONTENT_INFO);
        FP.add(UIDFolder.FetchProfileItem.UID);
        FP.add("Message-ID");
        FP.add("X-Priority");
        draftsFP.add(FetchProfile.Item.ENVELOPE);
        draftsFP.add(FetchProfile.Item.FLAGS);
        draftsFP.add(FetchProfile.Item.CONTENT_INFO);
        draftsFP.add(UIDFolder.FetchProfileItem.UID);
        draftsFP.add("Message-ID");
        draftsFP.add("X-Priority");
        draftsFP.add(HEADER_SONICLE_FROM_DRAFTER);
        if (hasDmsDocumentArchiving()) {
            FP.add("X-WT-Archived");
            draftsFP.add("X-WT-Archived");
        }
        pecFP.add(FetchProfile.Item.ENVELOPE);
        pecFP.add(FetchProfile.Item.FLAGS);
        pecFP.add(FetchProfile.Item.CONTENT_INFO);
        pecFP.add(UIDFolder.FetchProfileItem.UID);
        pecFP.add("Message-ID");
        pecFP.add("X-Priority");
        pecFP.add(HDR_PEC_PROTOCOLLO);
        pecFP.add(HDR_PEC_RIFERIMENTO_MESSAGE_ID);
        pecFP.add(HDR_PEC_TRASPORTO);
        pecFP.add(HDR_PEC_RICEVUTA);
        pecFP.add(HDR_PEC_TIPORICEVUTA);

        sortfolders = ss.isSortFolder();

        mainAccount.setDifferentDefaultFolder(us.getDefaultFolder());

        mainAccount.setMailSession(environment.getSession().getMailSession());

        mainAccount.setPort(mprofile.getMailPort());
        mainAccount.setHost(mprofile.getMailHost());
        mainAccount.setUsername(mprofile.getMailUsername());
        mainAccount.setPassword(mprofile.getMailPassword());
        mainAccount.setReplyTo(mprofile.getReplyTo());

        if (isImpersonated) {
            if (vmailSecret == null)
                mainAccount.setSaslRFCImpersonate(mprofile.getMailUsername(), ss.getAdminUser(),
                        ss.getAdminPassword());
            else
                mainAccount.setNethImpersonate(mprofile.getMailUsername(), vmailSecret);
        }
        mainAccount.setFolderSent(mprofile.getFolderSent());
        mainAccount.setFolderDrafts(mprofile.getFolderDrafts());
        mainAccount.setFolderSpam(mprofile.getFolderSpam());
        mainAccount.setFolderTrash(mprofile.getFolderTrash());
        mainAccount.setFolderArchive(mprofile.getFolderArchive());

        //TODO initialize user for first time use
        //SettingsManager sm = wta.getSettingsManager();
        //boolean initUser = LangUtils.value(sm.getUserSetting(profile, "mail", com.sonicle.webtop.Settings.INITIALIZE), false);
        //if(initUser) initializeUser(profile);

        mft = new MailFoldersThread(this, environment, mainAccount);
        mft.setCheckAll(mprofile.isScanAll());
        mft.setSleepInbox(mprofile.getScanSeconds());
        mft.setSleepCycles(mprofile.getScanCycles());
        try {
            loadTags();

            mft.abort();
            mainAccount.checkStoreConnected();

            //prepare special folders if not existant
            if (ss.isAutocreateSpecialFolders()) {
                mainAccount.createSpecialFolders();
            }

            mainAccount.setSkipReplyFolders(new String[] { mainAccount.getFolderDrafts(),
                    mainAccount.getFolderSent(), mainAccount.getFolderSpam(), mainAccount.getFolderTrash(),
                    mainAccount.getFolderArchive() });
            mainAccount.setSkipForwardFolders(
                    new String[] { mainAccount.getFolderSpam(), mainAccount.getFolderTrash(), });

            mainAccount.loadFoldersCache(mft, false);
            //if (!mainAccount.getMailSession().getDebug())
            mft.start();

            vfsmanager = (IVfsManager) WT.getServiceManager("com.sonicle.webtop.vfs");
            //cloud uploads goes here
            registerUploadListener("UploadCloudFile", new OnUploadCloudFile());

            if (!profile.getPrincipal().getAuthenticationDomain().getDirUri().getScheme()
                    .equals(LdapNethDirectory.SCHEME))
                setSharedSeen(mainAccount, us.isSharedSeen());

            //if external archive, initialize account
            if (ss.isArchivingExternal()) {
                archiveAccount = createAccount(ARCHIVE_ACCOUNT_ID);

                //MailStore support dropped for poor imap implementation
                //
                //if (ss.getArchivingExternalType().equals("mailstore")) {
                //   String defaultFolder=us.getArchiveExternalUserFolder();
                //   if (defaultFolder==null || defaultFolder.trim().length()==0)
                //      defaultFolder=profile.getEmailAddress();
                //   //MailStore produces a strange tree with two times the same account name
                //   archiveAccount.setDifferentDefaultFolder(defaultFolder+"/"+defaultFolder);
                //}

                //defaults to WebTop External Archive
                archiveAccount.setHasInboxFolder(true); //archive copy creates INBOX folder under user archive
                String defaultFolder = us.getArchiveExternalUserFolder();
                if (defaultFolder == null || defaultFolder.trim().length() == 0)
                    defaultFolder = profile.getUserId();
                archiveAccount.setDifferentDefaultFolder(defaultFolder);

                archiveAccount.setFolderPrefix(ss.getArchivingExternalFolderPrefix());
                archiveAccount.setProtocol(ss.getArchivingExternalProtocol());

                archiveAccount.setMailSession(environment.getSession().getMailSession());

                archiveAccount.setPort(ss.getArchivingExternalPort());
                archiveAccount.setHost(ss.getArchivingExternalHost());
                archiveAccount.setUsername(ss.getArchivingExternalUsername());
                archiveAccount.setPassword(ss.getArchivingExternalPassword());

                archiveAccount.setFolderSent(mprofile.getFolderSent());
                archiveAccount.setFolderDrafts(mprofile.getFolderDrafts());
                archiveAccount.setFolderSpam(mprofile.getFolderSpam());
                archiveAccount.setFolderTrash(mprofile.getFolderTrash());
                archiveAccount.setFolderArchive(mprofile.getFolderArchive());
            }

            //add any configured external account
            for (ExternalAccount extacc : mailManager.listExternalAccounts()) {
                String id = extacc.getExternalAccountId().toString();
                externalAccountsMap.put(id, extacc);

                MailAccount acct = createAccount(id);
                acct.setFolderPrefix(extacc.getFolderPrefix());
                acct.setProtocol(extacc.getProtocol());

                acct.setMailSession(environment.getSession().getMailSession());

                acct.setPort(extacc.getPort());
                acct.setHost(extacc.getHost());
                acct.setUsername(extacc.getUserName());
                acct.setPassword(extacc.getPassword());

                acct.setFolderSent(extacc.getFolderSent());
                acct.setFolderDrafts(extacc.getFolderDrafts());
                acct.setFolderSpam(extacc.getFolderSpam());
                acct.setFolderTrash(extacc.getFolderTrash());
                acct.setFolderArchive(extacc.getFolderArchive());
                acct.setReadOnly(extacc.isReadOnly());

                externalAccounts.add(acct);

                MailFoldersThread xmft = new MailFoldersThread(this, environment, acct);
                xmft.setInboxOnly(true);

                acct.setFoldersThread(xmft);
                acct.checkStoreConnected();
                acct.loadFoldersCache(xmft, false);

                //MFT start postponed to first processGetFavoritesTree
            }

        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
        }
    }

    private MailAccount createAccount(String id) {
        MailAccount account = new MailAccount(id, this, environment);
        accounts.put(id, account);
        return account;
    }

    public MailAccount getMainAccount() {
        return mainAccount;
    }

    public MailAccount getArchiveAccount() {
        return archiveAccount;
    }

    private synchronized void loadTags() throws WTException {
        atags.clear();
        htags.clear();
        atags = mailManager.getTags();
        for (Tag tag : atags) {
            htags.put(tag.getTagId(), tag);
        }
    }

    public MailServiceSettings getMailServiceSettings() {
        return this.ss;
    }

    public MailUserSettings getMailUserSettings() {
        return this.us;
    }

    public CoreUserSettings getCoreUserSettings() {
        return this.cus;
    }

    /**
     * Logout
     *
     */
    public boolean logout() {
        // Disconnect from email server
        cleanup();
        return true;
    }

    public FetchProfile getMessageFetchProfile() {
        return FP;
    }

    public MailFoldersThread getMailFoldersThread() {
        return mft;
    }

    public void dmsArchiveMessages(FolderCache from, long nuids[], String idcategory, String idsubcategory,
            boolean fullthreads) throws MessagingException, FileNotFoundException, IOException {
        UserProfile profile = environment.getProfile();
        String archiveto = ss.getDmsArchivePath();
        for (long nuid : nuids) {
            Message msg = from.getMessage(nuid);

            String id = getMessageID(msg);
            if (id.startsWith("<")) {
                id = id.substring(1, id.length() - 1);
            }
            id = id.replaceAll("\\\\", "_");
            id = id.replaceAll("/", "_");
            String filename = archiveto + "/" + id + ".eml";
            String txtname = archiveto + "/" + id + ".txt";
            File file = new File(filename);
            File txtfile = new File(txtname);
            //Only if spool file does not exists
            if (!file.exists()) {

                String emailfrom = "nomail@nodomain.it";
                Address a[] = msg.getFrom();
                if (a != null && a.length > 0) {
                    InternetAddress sender = ((InternetAddress) a[0]);
                    emailfrom = sender.getAddress();
                }
                String emailto = "nomail@nodomain.it";
                a = msg.getRecipients(Message.RecipientType.TO);
                if (a != null && a.length > 0) {
                    InternetAddress to = ((InternetAddress) a[0]);
                    emailto = to.getAddress();
                }
                String subject = msg.getSubject();
                java.util.Date date = msg.getReceivedDate();
                java.util.Calendar cal = java.util.Calendar.getInstance();
                cal.setTime(date);
                int dd = cal.get(java.util.Calendar.DAY_OF_MONTH);
                int mm = cal.get(java.util.Calendar.MONTH) + 1;
                int yyyy = cal.get(java.util.Calendar.YEAR);
                String sdd = dd < 10 ? "0" + dd : "" + dd;
                String smm = mm < 10 ? "0" + mm : "" + mm;
                String syyyy = "" + yyyy;

                FileOutputStream fos = new FileOutputStream(file);
                msg.writeTo(fos);
                fos.close();

                PrintWriter pw = new PrintWriter(txtfile);
                pw.println(emailfrom);
                pw.println(emailto);
                pw.println(subject);
                pw.println(sdd + "/" + smm + "/" + syyyy);
                pw.println(profile.getUserId());
                pw.println(idcategory);
                pw.println(idsubcategory);
                pw.close();
            }
        }
        from.markDmsArchivedMessages(nuids, fullthreads);
    }

    private void deleteAutosavedDraft(MailAccount account, long msgId) {
        int retry = 3;
        while (retry > 0) {
            try {
                account.deleteByHeaderValue(HEADER_X_WEBTOP_MSGID, "" + msgId);
                if (retry < 3)
                    logger.error("Retry deleting automatic draft succeded");
                break;
            } catch (Throwable t) {
                logger.error("Error deleting automatic draft", t);
            }
            if (--retry > 0) {
                logger.error("Retrying delete of automatic draft after exception");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException exc) {
                }
            }
        }
        if (retry == 0)
            logger.error("Delete of automatic draft failed");
    }

    private MailAccount getAccount(Identity ident) {
        if (ident == null)
            return mainAccount;
        MailAccount account = ident.getAccount();
        return account != null ? account : mainAccount;
    }

    protected InputStream getAttachmentInputStream(String accountId, String foldername, long uidmessage,
            int idattach) throws MessagingException, IOException {
        MailAccount account = getAccount(accountId);
        FolderCache fc = account.getFolderCache(foldername);
        Message m = fc.getMessage(uidmessage);

        HTMLMailData mailData = fc.getMailData((MimeMessage) m);
        return mailData.getAttachmentPart(idattach).getInputStream();
    }

    public void processManageAutosave(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        super.processManageAutosave(request, response, out);
        long msgId = Long.parseLong(request.getParameter("key"));
        String value = request.getParameter("value");
        JsMailAutosave jsmas = JsonResult.GSON_PLAIN_WONULLS.fromJson(value, JsMailAutosave.class);

        //save also in drafts
        try {
            Identity id = mailManager.findIdentity(jsmas.identityId);
            MailAccount account = getAccount(id);
            JsMessage jsmsg = new JsMessage();
            jsmsg.content = jsmas.content;
            jsmsg.folder = jsmas.folder;
            jsmsg.format = jsmas.format;
            jsmsg.forwardedfolder = jsmas.forwardedfolder;
            jsmsg.forwardedfrom = jsmas.forwardedfrom;
            jsmsg.from = id.getDisplayName() + " <" + id.getEmail() + ">";
            jsmsg.identityId = jsmas.identityId;
            jsmsg.inreplyto = jsmas.inreplyto;
            jsmsg.origuid = jsmas.origuid;
            jsmsg.priority = jsmas.priority;
            jsmsg.receipt = jsmas.receipt;
            jsmsg.recipients = new ArrayList<JsRecipient>();
            for (int r = 0; r < jsmas.recipients.size(); ++r) {
                JsRecipient jsr = new JsRecipient();
                jsr.email = jsmas.recipients.get(r);
                jsr.rtype = jsmas.rtypes.get(r);
                jsmsg.recipients.add(jsr);
            }
            jsmsg.references = jsmas.references;
            jsmsg.replyfolder = jsmas.replyfolder;
            jsmsg.subject = jsmas.subject;

            SimpleMessage msg = prepareMessage(jsmsg, msgId, false, false);
            account.checkStoreConnected();
            FolderCache fc = account.getFolderCache(account.getFolderDrafts());

            //find and delete old draft for this msgid
            account.deleteByHeaderValue(HEADER_X_WEBTOP_MSGID, "" + msgId);

            msg.addHeaderLine(HEADER_X_WEBTOP_MSGID + ": " + msgId);
            Exception exc = saveMessage(msg, null, fc);
        } catch (Exception exc) {
            logger.error("Error on autosave in drafts!", exc);
        }
    }

    public void moveMessages(FolderCache from, FolderCache to, long uids[], boolean fullthreads)
            throws MessagingException {
        from.moveMessages(uids, to, fullthreads);
    }

    public void copyMessages(FolderCache from, FolderCache to, long uids[], boolean fullthreads)
            throws MessagingException, IOException {
        from.copyMessages(uids, to, fullthreads);
    }

    public void deleteMessages(FolderCache from, long uids[], boolean fullthreads) throws MessagingException {
        from.deleteMessages(uids, fullthreads);
    }

    public void archiveMessages(FolderCache from, String folderarchive, long uids[], boolean fullthreads)
            throws MessagingException {
        from.archiveMessages(uids, folderarchive, fullthreads);
    }

    public void flagMessages(FolderCache from, long uids[], String flag) throws MessagingException {
        from.flagMessages(uids, flag);
    }

    public void tagMessages(FolderCache from, long uids[], String tagId) throws MessagingException {
        from.tagMessages(uids, tagId);
    }

    public void untagMessages(FolderCache from, long uids[], String tagId) throws MessagingException {
        from.untagMessages(uids, tagId);
    }

    public void clearMessagesTags(FolderCache from, long uids[]) throws MessagingException {
        from.clearMessagesTags(uids);
    }

    public void clearMessagesFlag(FolderCache from, long uids[]) throws MessagingException {
        from.clearMessagesFlag(uids);
    }

    public void setMessagesSeen(FolderCache from, long uids[]) throws MessagingException {
        from.setMessagesSeen(uids);
    }

    public void setMessagesUnseen(FolderCache from, long uids[]) throws MessagingException {
        from.setMessagesUnseen(uids);
    }

    public void hideFolder(MailAccount account, String foldername) throws MessagingException {
        Folder folder = account.getFolder(foldername);
        try {
            folder.close(false);
        } catch (Throwable exc) {
        }
        account.destroyFolderCache(foldername);
        if (account == mainAccount)
            us.setFolderHidden(foldername, true);
        else
            us.setFolderHidden(mainAccount.getId() + "_" + foldername, true);
    }

    public boolean isFolderHidden(MailAccount account, String foldername) {
        if (account == mainAccount)
            return us.isFolderHidden(foldername);
        else
            return us.isFolderHidden(mainAccount.getId() + "_" + foldername);
    }

    private InternetAddress getInternetAddress(String email) throws UnsupportedEncodingException, AddressException {
        email = email.trim();
        if (!email.startsWith("\"")) {
            int ix = email.indexOf("<");
            if (ix >= 0) {
                String personal = email.substring(0, ix).trim();
                String address = email.substring(ix).trim();
                email = "\"" + personal + "\" " + address;
            }
        }
        InternetAddress ia[] = InternetAddress.parse(email, false);
        //build an InternetAddress with UTF8 personal, if present
        InternetAddress retia = InternetAddressUtils.toInternetAddress(ia[0].getAddress(), ia[0].getPersonal());
        return retia;
    }

    public Identity findIdentity(InternetAddress fromAddr) {
        for (Identity ident : mprofile.getIdentities()) {
            if (fromAddr.getAddress().equalsIgnoreCase(ident.getEmail()))
                return ident;
        }
        return null;
    }

    public Exception sendReceipt(Identity ident, String from, String to, String subject, String body) {
        return sendTextMsg(ident, from, from, new String[] { to }, null, null, "Receipt: " + subject, body);
    }

    private Exception sendTextMsg(Identity ident, String fromAddr, String rplyAddr, String[] toAddr,
            String[] ccAddr, String[] bccAddr, String subject, String body) {

        return sendTextMsg(ident, fromAddr, rplyAddr, toAddr, ccAddr, bccAddr, subject, body, null);

    }

    private Exception sendTextMsg(Identity ident, String fromAddr, String rplyAddr, String[] toAddr,
            String[] ccAddr, String[] bccAddr, String subject, String body, List<JsAttachment> attachments) {

        SimpleMessage smsg = new SimpleMessage(0);

        //set the TO recipients
        smsg.addTo(toAddr);

        //set the CC recipients
        smsg.addCc(ccAddr);

        //set BCC recipients
        smsg.addBcc(bccAddr);

        if (ident != null)
            smsg.setFrom(ident);

        //set Reply To address
        if (rplyAddr != null && rplyAddr.length() > 0) {
            smsg.setReplyTo(rplyAddr);

            //set the subject
        }
        smsg.setSubject(subject);

        //set the content
        smsg.setContent(body);

        return sendMsg(fromAddr, smsg, attachments);

    }

    public boolean sendMsg(InternetAddress from, Collection<InternetAddress> to, Collection<InternetAddress> cc,
            Collection<InternetAddress> bcc, String subject, MimeMultipart part) {
        return sendMsg(null, from, to, cc, bcc, subject, part);
    }

    public boolean sendMsg(Identity ident, InternetAddress from, Collection<InternetAddress> to,
            Collection<InternetAddress> cc, Collection<InternetAddress> bcc, String subject, MimeMultipart part) {

        try {
            subject = MimeUtility.encodeText(subject);
        } catch (Exception ex) {
        }

        try {
            MailAccount account = getAccount(ident);

            MimeMessage message = new MimeMessage(account.getMailSession());
            message.setSubject(subject);
            message.addFrom(new InternetAddress[] { from });

            if (to != null) {
                for (InternetAddress ia : to)
                    message.addRecipient(Message.RecipientType.TO, ia);
            }
            if (cc != null) {
                for (InternetAddress ia : cc)
                    message.addRecipient(Message.RecipientType.CC, ia);
            }
            if (bcc != null) {
                for (InternetAddress ia : bcc)
                    message.addRecipient(Message.RecipientType.BCC, ia);
            }

            message.setContent(part);
            message.setSentDate(new java.util.Date());

            return sendMsg(ident, message);

        } catch (MessagingException ex) {
            logger.warn("Unable to send message", ex);
            return false;
        }
    }

    public boolean sendMsg(Message msg) {
        return sendMsg(null, msg);
    }

    public boolean sendMsg(Identity ident, Message msg) {
        if (ident == null) {
            try {
                ident = findIdentity((InternetAddress) (msg.getFrom()[0]));
            } catch (Exception exc) {

            }
        }
        String sentfolder = getSentFolder(ident);
        MailAccount account = getAccount(ident);
        try {
            Transport.send(msg);
            saveSent(account, msg, sentfolder);
            return true;

        } catch (Exception ex) {
            Service.logger.error("Exception", ex);
            return false;
        }

    }

    public String getSentFolder(Identity ident) {
        UserProfile profile = environment.getProfile();
        String sentfolder = mainAccount.getFolderSent();
        if (ident != null) {
            MailAccount account = getAccount(ident);
            String mainfolder = ident.getMainFolder();
            if (mainfolder != null && mainfolder.trim().length() > 0) {
                String newsentfolder = mainfolder + account.getFolderSeparator()
                        + account.getLastFolderName(sentfolder);
                try {
                    Folder folder = account.getFolder(newsentfolder);
                    if (folder.exists()) {
                        sentfolder = newsentfolder;
                    }
                } catch (MessagingException exc) {
                    logger.error("Error on identity {}/{} Sent Folder", profile.getUserId(), ident.getEmail(), exc);
                }
            }
        }

        return sentfolder;
    }

    public Exception sendMsg(String from, SimpleMessage smsg, List<JsAttachment> attachments) {
        //UserProfile profile = environment.getProfile();
        //String sentfolder = mprofile.getFolderSent();
        Identity ident = smsg.getFrom();
        /*f (ident != null ) {
           String mainfolder=ident.getMainFolder();
           if (mainfolder != null && mainfolder.trim().length() > 0) {
        String newsentfolder = mainfolder + folderSeparator + getLastFolderName(sentfolder);
        try {
           Folder folder = getFolder(newsentfolder);
           if (folder.exists()) {
              sentfolder = newsentfolder;
           }
        } catch (MessagingException exc) {
           logger.error("Error on identity {}/{} Sent Folder", profile.getUserId(), ident.getEmail(), exc);
        }
           }
        }*/
        String sentfolder = getSentFolder(ident);
        MailAccount account = getAccount(ident);
        Exception retexc = null;
        Message msg = null;
        try {
            msg = createMessage(from, smsg, attachments, false);
            Transport.send(msg);
        } catch (Exception ex) {
            Service.logger.error("Exception", ex);
            retexc = ex;
        }

        //if sent succesful, save outgoing message
        if (retexc == null && msg != null) {
            retexc = saveSent(account, msg, sentfolder);

        }
        return retexc;

    } //end sendMsg, SimpleMessage version

    public Exception sendMessage(SimpleMessage msg, List<JsAttachment> attachments) {
        UserProfile profile = environment.getProfile();
        String sender = profile.getEmailAddress();
        String name = profile.getDisplayName();
        Identity from = msg.getFrom();
        String replyto = getAccount(from).getReplyTo();
        boolean isOtherIdentity = false;

        if (from != null) {
            isOtherIdentity = !from.isMainIdentity();
            sender = from.getEmail();
            name = from.getDisplayName();
            if (replyto == null || replyto.trim().length() == 0)
                replyto = sender;
        }

        if (name != null && name.length() > 0) {
            sender = name + " <" + sender + ">";
            if (!isOtherIdentity
                    && (replyto != null && replyto.trim().length() > 0 && !replyto.trim().endsWith(">")))
                replyto = name + " <" + replyto + ">";
        }
        if (!isOtherIdentity && (replyto != null && replyto.trim().length() > 0))
            msg.setReplyTo(replyto);

        Exception retexc = null;

        if (attachments.size() == 0 && msg.getAttachments() == null) {
            retexc = sendMsg(sender, msg, null);
        } else {

            retexc = sendMsg(sender, msg, attachments);

            if (retexc == null)
                clearCloudAttachments(msg.getId());
        }

        return retexc;
    }

    public Message createMessage(String from, SimpleMessage smsg, List<JsAttachment> attachments, boolean tosave)
            throws Exception {
        MimeMessage msg = null;
        boolean success = true;

        String[] to = SimpleMessage.breakAddr(smsg.getTo());
        String[] cc = SimpleMessage.breakAddr(smsg.getCc());
        String[] bcc = SimpleMessage.breakAddr(smsg.getBcc());
        String replyTo = smsg.getReplyTo();

        msg = new MimeMessage(mainAccount.getMailSession());
        msg.setFrom(getInternetAddress(from));
        InternetAddress ia = null;

        //set the TO recipient
        for (int q = 0; q < to.length; q++) {
            //        Service.logger.debug("to["+q+"]="+to[q]);
            to[q] = to[q].replace(',', ' ');
            try {
                ia = getInternetAddress(to[q]);
            } catch (AddressException exc) {
                throw new AddressException(to[q]);
            }
            msg.addRecipient(Message.RecipientType.TO, ia);
        }

        //set the CC recipient
        for (int q = 0; q < cc.length; q++) {
            cc[q] = cc[q].replace(',', ' ');
            try {
                ia = getInternetAddress(cc[q]);
            } catch (AddressException exc) {
                throw new AddressException(cc[q]);
            }
            msg.addRecipient(Message.RecipientType.CC, ia);
        }

        //set BCC recipients
        for (int q = 0; q < bcc.length; q++) {
            bcc[q] = bcc[q].replace(',', ' ');
            try {
                ia = getInternetAddress(bcc[q]);
            } catch (AddressException exc) {
                throw new AddressException(bcc[q]);
            }
            msg.addRecipient(Message.RecipientType.BCC, ia);
        }

        //set reply to addr
        if (replyTo != null && replyTo.length() > 0) {
            Address[] replyaddr = new Address[1];
            replyaddr[0] = getInternetAddress(replyTo);
            msg.setReplyTo(replyaddr);
        }

        //add any header
        String headerLines[] = smsg.getHeaderLines();
        for (int i = 0; i < headerLines.length; ++i) {
            if (!headerLines[i].startsWith("Sonicle-reply-folder")) {
                msg.addHeaderLine(headerLines[i]);
            }
        }

        //add reply/references
        String inreplyto = smsg.getInReplyTo();
        String references[] = smsg.getReferences();
        String replyfolder = smsg.getReplyFolder();
        if (inreplyto != null) {
            msg.setHeader("In-Reply-To", inreplyto);
        }
        if (references != null && references[0] != null) {
            msg.setHeader("References", references[0]);
        }
        if (tosave) {
            if (replyfolder != null) {
                msg.setHeader("Sonicle-reply-folder", replyfolder);
            }
            msg.setHeader("Sonicle-draft", "true");
        }

        //add forward data
        String forwardedfrom = smsg.getForwardedFrom();
        String forwardedfolder = smsg.getForwardedFolder();
        if (forwardedfrom != null) {
            msg.setHeader("Forwarded-From", forwardedfrom);
        }
        if (tosave) {
            if (forwardedfolder != null) {
                msg.setHeader("Sonicle-forwarded-folder", forwardedfolder);
            }
            msg.setHeader("Sonicle-draft", "true");
        }
        //set the subject
        String subject = smsg.getSubject();
        try {
            //subject=MimeUtility.encodeText(smsg.getSubject(), "ISO-8859-1", null);
            subject = MimeUtility.encodeText(smsg.getSubject());
        } catch (Exception exc) {
        }
        msg.setSubject(subject);

        //set priority
        int priority = smsg.getPriority();
        if (priority != 3) {
            msg.setHeader("X-Priority", "" + priority);

            //set receipt
        }
        String receiptTo = from;
        try {
            receiptTo = MimeUtility.encodeText(from, "ISO-8859-1", null);
        } catch (Exception exc) {
        }
        if (smsg.getReceipt()) {
            msg.setHeader("Disposition-Notification-To", from);

            //see if there are any new attachments for the message
        }
        int noAttach;
        int newAttach;

        if (attachments == null) {
            newAttach = 0;
        } else {
            newAttach = attachments.size();
        }

        //get the array of the old attachments
        Part[] oldParts = smsg.getAttachments();

        //check if there are old attachments
        if (oldParts == null) {
            noAttach = 0;
        } else { //old attachments exist
            noAttach = oldParts.length;
        }

        if ((newAttach > 0) || (noAttach > 0) || !smsg.getMime().equalsIgnoreCase("text/plain")) {
            // create the main Multipart
            MimeMultipart mp = new MimeMultipart("mixed");
            MimeMultipart unrelated = null;

            String textcontent = smsg.getTextContent();
            //if is text, or no alternative text is available, add the content as one single body part
            //else create a multipart/alternative with both rich and text mime content
            if (textcontent == null || smsg.getMime().equalsIgnoreCase("text/plain")) {
                MimeBodyPart mbp1 = new MimeBodyPart();
                mbp1.setContent(smsg.getContent(), MailUtils.buildPartContentType(smsg.getMime(), "UTF-8"));
                mp.addBodyPart(mbp1);
            } else {
                MimeMultipart alternative = new MimeMultipart("alternative");
                //the rich part
                MimeBodyPart mbp2 = new MimeBodyPart();
                mbp2.setContent(smsg.getContent(), MailUtils.buildPartContentType(smsg.getMime(), "UTF-8"));
                //the text part
                MimeBodyPart mbp1 = new MimeBodyPart();

                /*          ByteArrayOutputStream bos=new ByteArrayOutputStream(textcontent.length());
                 com.sun.mail.util.QPEncoderStream qpe=new com.sun.mail.util.QPEncoderStream(bos);
                 for(int i=0;i<textcontent.length();++i) {
                 try {
                 qpe.write(textcontent.charAt(i));
                 } catch(IOException exc) {
                 Service.logger.error("Exception",exc);
                 }
                 }
                 textcontent=new String(bos.toByteArray());*/
                mbp1.setContent(textcontent, MailUtils.buildPartContentType("text/plain", "UTF-8"));
                //          mbp1.setHeader("Content-transfer-encoding","quoted-printable");

                alternative.addBodyPart(mbp1);
                alternative.addBodyPart(mbp2);

                MimeBodyPart altbody = new MimeBodyPart();
                altbody.setContent(alternative);

                mp.addBodyPart(altbody);
            }

            if (noAttach > 0) { //if there are old attachments
                // create the parts with the attachments
                //MimeBodyPart[] mbps2 = new MimeBodyPart[noAttach];
                //Part[] mbps2 = new Part[noAttach];

                //for(int e = 0;e < noAttach;e++) {
                //  mbps2[e] = (Part)oldParts[e];
                //}//end for e
                //add the old attachment parts
                for (int r = 0; r < noAttach; r++) {
                    Object content = null;
                    String contentType = null;
                    String contentFileName = null;
                    if (oldParts[r] instanceof Message) {
                        //                Service.logger.debug("Attachment is a message");
                        Message msgpart = (Message) oldParts[r];
                        MimeMessage mm = new MimeMessage(mainAccount.getMailSession());
                        mm.addFrom(msgpart.getFrom());
                        mm.setRecipients(Message.RecipientType.TO, msgpart.getRecipients(Message.RecipientType.TO));
                        mm.setRecipients(Message.RecipientType.CC, msgpart.getRecipients(Message.RecipientType.CC));
                        mm.setRecipients(Message.RecipientType.BCC,
                                msgpart.getRecipients(Message.RecipientType.BCC));
                        mm.setReplyTo(msgpart.getReplyTo());
                        mm.setSentDate(msgpart.getSentDate());
                        mm.setSubject(msgpart.getSubject());
                        mm.setContent(msgpart.getContent(), msgpart.getContentType());
                        content = mm;
                        contentType = "message/rfc822";
                    } else {
                        //                Service.logger.debug("Attachment is not a message");
                        content = oldParts[r].getContent();
                        if (!(content instanceof MimeMultipart)) {
                            contentType = oldParts[r].getContentType();
                            contentFileName = oldParts[r].getFileName();
                        }
                    }
                    MimeBodyPart mbp = new MimeBodyPart();
                    if (contentFileName != null) {
                        mbp.setFileName(contentFileName);
                        //              Service.logger.debug("adding attachment mime "+contentType+" filename "+contentFileName);
                        contentType += "; name=\"" + contentFileName + "\"";
                    }
                    if (content instanceof MimeMultipart)
                        mbp.setContent((MimeMultipart) content);
                    else
                        mbp.setDataHandler(new DataHandler(content, contentType));
                    mp.addBodyPart(mbp);
                }

            } //end if, adding old attachments

            if (newAttach > 0) { //if there are new attachments
                // create the parts with the attachments
                MimeBodyPart[] mbps = new MimeBodyPart[newAttach];

                for (int e = 0; e < newAttach; e++) {
                    mbps[e] = new MimeBodyPart();

                    // attach the file to the message
                    JsAttachment attach = (JsAttachment) attachments.get(e);
                    UploadedFile upfile = getUploadedFile(attach.uploadId);
                    FileDataSource fds = new FileDataSource(upfile.getFile());
                    mbps[e].setDataHandler(new DataHandler(fds));
                    // filename starts has format:
                    // "_" + userid + sessionId + "_" + filename
                    //
                    if (attach.inline) {
                        mbps[e].setDisposition(Part.INLINE);
                    }
                    String contentFileName = attach.fileName.trim();
                    mbps[e].setFileName(contentFileName);
                    String contentType = upfile.getMediaType() + "; name=\"" + contentFileName + "\"";
                    mbps[e].setHeader("Content-type", contentType);
                    if (attach.cid != null && attach.cid.trim().length() > 0) {
                        mbps[e].setHeader("Content-ID", "<" + attach.cid + ">");
                        mbps[e].setHeader("X-Attachment-Id", attach.cid);
                        mbps[e].setDisposition(Part.INLINE);
                        if (unrelated == null)
                            unrelated = new MimeMultipart("mixed");
                    }
                } //end for e

                //add the new attachment parts
                if (unrelated == null) {
                    for (int r = 0; r < newAttach; r++)
                        mp.addBodyPart(mbps[r]);
                } else {
                    //mp becomes the related part with text parts and cids
                    MimeMultipart related = mp;
                    related.setSubType("related");
                    //nest the related part into the mixed part
                    mp = unrelated;
                    MimeBodyPart mbd = new MimeBodyPart();
                    mbd.setContent(related);
                    mp.addBodyPart(mbd);
                    for (int r = 0; r < newAttach; r++) {
                        if (mbps[r].getHeader("Content-ID") != null) {
                            related.addBodyPart(mbps[r]);
                        } else {
                            mp.addBodyPart(mbps[r]);
                        }
                    }
                }

            } //end if, adding new attachments

            //
            //          msg.addHeaderLine("This is a multi-part message in MIME format.");
            // add the Multipart to the message
            msg.setContent(mp);

        } else { //end if newattach
            msg.setText(smsg.getContent());
        } //singlepart message

        msg.setSentDate(new java.util.Date());

        return msg;

    }

    private Exception saveSent(MailAccount account, Message msg, String sentfolder) {
        Exception retexc = null;
        try {
            account.checkCreateFolder(sentfolder);
            Folder outgoing = account.getFolder(sentfolder);
            msg.setFlag(Flags.Flag.SEEN, true);

            Message[] saveMsgs = new MimeMessage[1];
            saveMsgs[0] = msg;

            outgoing.appendMessages(saveMsgs);
        } catch (MessagingException exc) {
            Service.logger.error("Exception", exc);
        }

        return retexc;

    }

    public Exception saveMessage(SimpleMessage msg, List<JsAttachment> attachments, FolderCache fc) {
        Exception retexc = null;
        try {
            _saveMessage(msg, attachments, fc);
        } catch (Exception exc) {
            retexc = exc;
            logger.error("Error during saveMessage in " + fc.getFolderName(), exc);
        }
        return retexc;
    }

    public Exception scheduleMessage(SimpleMessage msg, List<JsAttachment> attachments, FolderCache fc,
            String senddate, String sendtime, String sendnotify) {
        Exception retexc = null;
        try {
            msg.addHeaderLine("Sonicle-send-scheduled: true");
            msg.addHeaderLine("Sonicle-send-date: " + senddate);
            msg.addHeaderLine("Sonicle-send-time: " + sendtime);
            msg.addHeaderLine("Sonicle-notify-delivery: " + sendnotify);
            Message m = _saveMessage(msg, attachments, fc);
            /*        String mid=m.getHeader("Message-ID")[0];
             MailServiceGeneral mservgen=(MailServiceGeneral)wta.getServiceGeneralByName("mail");
                
             UserProfile profile=wts.getEnvironment().getUserProfile();
             String port=profile.getMailPort()+"";
             String host=profile.getMailHost();
             String protocol=profile.getMailProtocol();
             String username=profile.getMailUsername();
             String folderprefix=profile.getFolderPrefix();
             String folderdrafts=profile.getFolderDrafts();
                
             MailServiceGeneral.MailUser mu=mservgen.createMailUser(profile.getIDDomain(), host, protocol, port, username, folderprefix, folderdrafts);
             mservgen.scheduleSendTask(mu, mid, senddate, sendtime, sendnotify);*/
        } catch (Exception exc) {
            retexc = exc;
        }
        return retexc;
    }

    private Message _saveMessage(SimpleMessage msg, List<JsAttachment> attachments, FolderCache fc)
            throws Exception {
        UserProfile profile = environment.getProfile();
        String sender = profile.getEmailAddress();
        String name = profile.getDisplayName();
        Identity from = msg.getFrom();
        String replyto = getAccount(from).getReplyTo();

        if (from != null) {
            sender = from.getEmail();
            name = from.getDisplayName();
            if (replyto == null || replyto.trim().length() == 0)
                replyto = sender;
        }

        msg.setReplyTo(replyto);

        if (name != null && name.length() > 0) {
            sender = name + " <" + sender + ">";

        }
        /*ArrayList<Attachment> origattachments = getAttachments(msg.getId());
        ArrayList<Attachment> attachments = new ArrayList<Attachment>();
        for (String attname : attnames) {
           for (Attachment att : origattachments) {
        if (att.getFile().getName().equals(attname)) {
           attachments.add(att);
           //Service.logger.debug("Adding attachment : "+attname+" -> "+att.getName());
           break;
        }
           }
        }*/

        Message newmsg = createMessage(sender, msg, attachments, true);
        newmsg.setHeader("Sonicle-draft", "true");
        //FolderCache fc=getFolderCache(profile.getFolderDrafts());
        //newmsg.writeTo(new FileOutputStream("C:/Users/gbulfon/Desktop/TEST.eml"));
        fc.save(newmsg);
        clearCloudAttachments(msg.getId());

        return newmsg;
    }

    private SimpleMessage getForwardMsg(long id, Message msg, boolean richContent, String fromtitle, String totitle,
            String cctitle, String datetitle, String subjecttitle, boolean attached) {
        Message forward = new MimeMessage(mainAccount.getMailSession());
        if (!attached) {
            try {
                StringBuffer htmlsb = new StringBuffer();
                StringBuffer textsb = new StringBuffer();
                boolean isHtml = appendReplyParts(msg, htmlsb, textsb, null);
                //      Service.logger.debug("isHtml="+isHtml);
                //      Service.logger.debug("richContent="+richContent);
                String html = "<HTML><BODY>" + htmlsb.toString() + "</BODY></HTML>";
                if (!richContent) {
                    forward.setText(getForwardBody(msg, textsb.toString(), SimpleMessage.FORMAT_TEXT, false,
                            fromtitle, totitle, cctitle, datetitle, subjecttitle));
                } else if (!isHtml) {
                    forward.setText(getForwardBody(msg, textsb.toString(), SimpleMessage.FORMAT_PREFORMATTED, false,
                            fromtitle, totitle, cctitle, datetitle, subjecttitle));
                } else {
                    forward.setText(MailUtils.removeMSWordShit(getForwardBody(msg, html, SimpleMessage.FORMAT_HTML,
                            true, fromtitle, totitle, cctitle, datetitle, subjecttitle)));
                }
            } catch (Exception exc) {
                Service.logger.error("Exception", exc);
            }
        }

        try {
            String msgid = null;
            String vh[] = msg.getHeader("Message-ID");
            if (vh != null) {
                msgid = vh[0];
            }
            if (msgid != null) {
                forward.setHeader("Forwarded-From", msgid);
            }
        } catch (MessagingException exc) {
            Service.logger.error("Exception", exc);
        }

        SimpleMessage fwd = new SimpleMessage(id, forward);
        fwd.setTo("");

        // Update appropriate subject
        // Fwd: subject
        try {
            String subject = msg.getSubject();
            if (subject == null) {
                subject = "";
            }
            if (!subject.toLowerCase().startsWith("fwd: ")) {
                fwd.setSubject("Fwd: " + subject);
            } else {
                fwd.setSubject(msg.getSubject());
            }
        } catch (MessagingException e) {
            Service.logger.error("Exception", e);
            //      Service.logger.debug("*** SimpleMessage: " +e);
        }

        return fwd;
    }

    private String getForwardBody(Message msg, String body, int format, boolean isHtml, String fromtitle,
            String totitle, String cctitle, String datetitle, String subjecttitle) throws MessagingException {
        UserProfile profile = environment.getProfile();
        Locale locale = profile.getLocale();
        String msgSubject = msg.getSubject();
        if (msgSubject == null) {
            msgSubject = "";
        }
        msgSubject = MailUtils.htmlescape(msgSubject);
        Address ad[] = msg.getFrom();
        String msgFrom = "";
        if (ad != null) {
            msgFrom = isHtml ? getHTMLDecodedAddress(ad[0]) : getDecodedAddress(ad[0]);
        }
        java.util.Date dt = msg.getSentDate();
        String msgDate = "";
        if (dt != null) {
            msgDate = DateFormat.getDateTimeInstance(java.text.DateFormat.LONG, java.text.DateFormat.LONG, locale)
                    .format(dt);
        }
        ad = msg.getRecipients(Message.RecipientType.TO);
        String msgTo = null;
        if (ad != null) {
            msgTo = "";
            for (int j = 0; j < ad.length; ++j) {
                msgTo += isHtml ? getHTMLDecodedAddress(ad[j]) : getDecodedAddress(ad[j]) + " ";
            }
        }
        ad = msg.getRecipients(Message.RecipientType.CC);
        String msgCc = null;
        if (ad != null) {
            msgCc = "";
            for (int j = 0; j < ad.length; ++j) {
                msgCc += isHtml ? getHTMLDecodedAddress(ad[j]) : getDecodedAddress(ad[j]) + " ";
            }
        }

        StringBuffer sb = new StringBuffer();
        String cr = "\n";
        if (format != SimpleMessage.FORMAT_TEXT) {
            cr = "<BR>";
        }
        if (format != SimpleMessage.FORMAT_HTML) {
            if (format == SimpleMessage.FORMAT_PREFORMATTED) {
                sb.append("<TT>");
            }
            sb.append(cr + cr + cr
                    + "----------------------------------------------------------------------------------" + cr
                    + cr);
            sb.append(fromtitle + ": " + msgFrom + cr);
            if (msgTo != null) {
                sb.append(totitle + ": " + msgTo + cr);
            }
            if (msgCc != null) {
                sb.append(cctitle + ": " + msgCc + cr);
            }
            sb.append(datetitle + ": " + msgDate + cr);
            sb.append(subjecttitle + ": " + msgSubject + cr + cr);
            if (format == SimpleMessage.FORMAT_PREFORMATTED) {
                sb.append("</TT>");
            }
        } else {
            sb.append(cr + "<HR>" + cr + cr);
            sb.append("<font face='Arial, Helvetica, sans-serif' size=2>");
            sb.append("<B>" + fromtitle + ":</B> " + msgFrom + "<BR>");
            if (msgTo != null) {
                sb.append("<B>" + totitle + ":</B> " + msgTo + "<BR>");
            }
            if (msgCc != null) {
                sb.append("<B>" + cctitle + ":</B> " + msgCc + "<BR>");
            }
            sb.append("<B>" + datetitle + ":</B> " + msgDate + "<BR>");
            sb.append("<B>" + subjecttitle + ":</B> " + msgSubject + "<BR>");
            sb.append("</font><br>" + cr);
        }

        // Prepend "> " for each line in the body
        //
        if (body != null) {
            if (format == SimpleMessage.FORMAT_HTML) {
                //        sb.append("<TABLE border=0 width='100%'><TR><td width=2 bgcolor=#000088></td><td width=2></td><td>");
                //        sb.append("<BLOCKQUOTE style='BORDER-LEFT: #000080 2px solid; MARGIN-LEFT: 5px; PADDING-LEFT: 5px'>");
            }
            if (!isHtml) {
                if (format == SimpleMessage.FORMAT_PREFORMATTED) {
                    //          sb.append("<BLOCKQUOTE style='BORDER-LEFT: #000080 2px solid; MARGIN-LEFT: 5px; PADDING-LEFT: 5px'>");
                    sb.append("<tt>");
                }
                StringTokenizer st = new StringTokenizer(body, "\n", true);
                while (st.hasMoreTokens()) {
                    String token = st.nextToken();
                    if (token.equals("\n")) {
                        sb.append(cr);
                    } else {
                        if (format == SimpleMessage.FORMAT_TEXT) {
                            sb.append("> ");
                        }
                        //sb.append(MailUtils.htmlescape(token));
                        sb.append(token);
                    }
                }
                if (format == SimpleMessage.FORMAT_PREFORMATTED) {
                    sb.append("</tt>");
                    //          sb.append("</BLOCKQUOTE>");
                }
            } else {
                //sb.append(getBodyInnerHtml(body));
                sb.append(body);
            }
            if (format == SimpleMessage.FORMAT_HTML) {
                //        sb.append("</td></tr></table>");
                //        sb.append("</BLOCKQUOTE>");
            }
        }
        return sb.toString();
    }

    private String getBodyInnerHtml(String body) {
        int ix1 = StringUtils.indexOfIgnoreCase(body, "<BODY");
        if (ix1 >= 0) {
            int ix2 = body.indexOf(">", ix1 + 1);
            if (ix2 < 0) {
                ix2 = ix1 + 4;
            }
            int ix3 = StringUtils.indexOfIgnoreCase(body, "</BODY", ix2 + 1);
            if (ix3 > 0) {
                body = body.substring(ix2 + 1, ix3);
            } else {
                body = body.substring(ix2 + 1);
            }
        }
        return body;
    }

    //Clone of MimeMessage that was private and used by my custom reply
    private Address[] eliminateDuplicates(Vector v, Address[] addrs) {
        if (addrs == null) {
            return null;
        }
        int gone = 0;
        for (int i = 0; i < addrs.length; i++) {
            boolean found = false;
            // search the vector for this address
            for (int j = 0; j < v.size(); j++) {
                if (((InternetAddress) v.elementAt(j)).equals(addrs[i])) {
                    // found it; count it and remove it from the input array
                    found = true;
                    gone++;
                    addrs[i] = null;
                    break;
                }
            }
            if (!found) {
                v.addElement(addrs[i]); // add new address to vector
            }
        }
        // if we found any duplicates, squish the array
        if (gone != 0) {
            Address[] a;
            // new array should be same type as original array
            // XXX - there must be a better way, perhaps reflection?
            if (addrs instanceof InternetAddress[]) {
                a = new InternetAddress[addrs.length - gone];
            } else {
                a = new Address[addrs.length - gone];
            }
            for (int i = 0, j = 0; i < addrs.length; i++) {
                if (addrs[i] != null) {
                    a[j++] = addrs[i];
                }
            }
            addrs = a;
        }
        return addrs;
    }

    //CLONE OF ImapMessage.reply() that does not set the ANSWERED Flag
    public Message reply(MailAccount account, MimeMessage orig, boolean replyToAll, boolean fromSent)
            throws MessagingException {
        MimeMessage reply = new MimeMessage(account.getMailSession());
        /*
         * Have to manipulate the raw Subject header so that we don't lose
         * any encoding information.  This is safe because "Re:" isn't
         * internationalized and (generally) isn't encoded.  If the entire
         * Subject header is encoded, prefixing it with "Re: " still leaves
         * a valid and correct encoded header.
         */
        String subject = orig.getHeader("Subject", null);
        if (subject != null) {
            if (!subject.regionMatches(true, 0, "Re: ", 0, 4)) {
                subject = "Re: " + subject;
            }
            reply.setHeader("Subject", subject);
        }
        Address a[] = null;
        if (!fromSent)
            a = orig.getReplyTo();
        else {
            Address ax[] = orig.getRecipients(RecipientType.TO);
            if (ax != null) {
                a = new Address[1];
                a[0] = ax[0];
            }
        }
        reply.setRecipients(Message.RecipientType.TO, a);
        if (replyToAll) {
            Vector v = new Vector();
            Session session = account.getMailSession();
            // add my own address to list
            InternetAddress me = InternetAddress.getLocalAddress(session);
            if (me != null) {
                v.addElement(me);
            }
            // add any alternate names I'm known by
            String alternates = null;
            if (session != null) {
                alternates = session.getProperty("mail.alternates");
            }
            if (alternates != null) {
                eliminateDuplicates(v, InternetAddress.parse(alternates, false));
            }
            // should we Cc all other original recipients?
            String replyallccStr = null;
            boolean replyallcc = false;
            if (session != null) {
                replyallcc = PropUtil.getBooleanSessionProperty(session, "mail.replyallcc", false);
            }
            // add the recipients from the To field so far
            eliminateDuplicates(v, a);
            a = orig.getRecipients(Message.RecipientType.TO);
            a = eliminateDuplicates(v, a);
            if (a != null && a.length > 0) {
                if (replyallcc) {
                    reply.addRecipients(Message.RecipientType.CC, a);
                } else {
                    reply.addRecipients(Message.RecipientType.TO, a);
                }
            }
            a = orig.getRecipients(Message.RecipientType.CC);
            a = eliminateDuplicates(v, a);
            if (a != null && a.length > 0) {
                reply.addRecipients(Message.RecipientType.CC, a);
            }
            // don't eliminate duplicate newsgroups
            a = orig.getRecipients(MimeMessage.RecipientType.NEWSGROUPS);
            if (a != null && a.length > 0) {
                reply.setRecipients(MimeMessage.RecipientType.NEWSGROUPS, a);
            }
        }

        String msgId = orig.getHeader("Message-Id", null);
        if (msgId != null) {
            reply.setHeader("In-Reply-To", msgId);
        }

        /*
         * Set the References header as described in RFC 2822:
         *
         * The "References:" field will contain the contents of the parent's
         * "References:" field (if any) followed by the contents of the parent's
         * "Message-ID:" field (if any).  If the parent message does not contain
         * a "References:" field but does have an "In-Reply-To:" field
         * containing a single message identifier, then the "References:" field
         * will contain the contents of the parent's "In-Reply-To:" field
         * followed by the contents of the parent's "Message-ID:" field (if
         * any).  If the parent has none of the "References:", "In-Reply-To:",
         * or "Message-ID:" fields, then the new message will have no
         * "References:" field.
         */
        String refs = orig.getHeader("References", " ");
        if (refs == null) {
            // XXX - should only use if it contains a single message identifier
            refs = orig.getHeader("In-Reply-To", " ");
        }
        if (msgId != null) {
            if (refs != null) {
                refs = MimeUtility.unfold(refs) + " " + msgId;
            } else {
                refs = msgId;
            }
        }
        if (refs != null) {
            reply.setHeader("References", MimeUtility.fold(12, refs));
        }

        //try {
        //    setFlags(answeredFlag, true);
        //} catch (MessagingException mex) {
        //    // ignore it
        //}
        return reply;
    }

    // used above in reply()
    private static final Flags answeredFlag = new Flags(Flags.Flag.ANSWERED);

    private SimpleMessage getReplyMsg(int id, MailAccount account, Message msg, boolean replyAll, boolean fromSent,
            boolean richContent, String myemail, boolean includeOriginal, String fromtitle, String totitle,
            String cctitle, String datetitle, String subjecttitle) {
        try {
            Message reply = reply(account, (MimeMessage) msg, replyAll, fromSent);

            removeDestination(reply, myemail);
            if (ss.getMessageReplyAllStripMyIdentities()) {
                for (Identity ident : mprofile.getIdentities()) {
                    removeDestination(reply, ident.getEmail());
                }
            }

            // Setup the message body
            //
            StringBuffer htmlsb = new StringBuffer();
            StringBuffer textsb = new StringBuffer();
            ArrayList<String> attnames = new ArrayList<String>();
            if (includeOriginal) {
                boolean isHtml = appendReplyParts(msg, htmlsb, textsb, attnames);
                String html = "<HTML><BODY>" + htmlsb.toString() + "</BODY></HTML>";
                String text = null;
                if (!richContent) {
                    text = getReplyBody(msg, textsb.toString(), SimpleMessage.FORMAT_TEXT, false, fromtitle,
                            totitle, cctitle, datetitle, subjecttitle, attnames);
                } else if (!isHtml) {
                    text = getReplyBody(msg, textsb.toString(), SimpleMessage.FORMAT_PREFORMATTED, false, fromtitle,
                            totitle, cctitle, datetitle, subjecttitle, attnames);
                } else {
                    text = getReplyBody(msg, html, SimpleMessage.FORMAT_HTML, true, fromtitle, totitle, cctitle,
                            datetitle, subjecttitle, attnames);
                }
                reply.setText(MailUtils.removeMSWordShit(text));
            } else {
                reply.setText("");
            }
            return new SimpleMessage(id, reply);
        } catch (MessagingException e) {
            Service.logger.error("Exception", e);
            //      Service.logger.debug("*** SimpleMessage: " + e);
            return null;
        } catch (IOException e) {
            Service.logger.error("Exception", e);
            //      Service.logger.debug("*** SimpleMessage: " + e);
            return null;
        }
    }

    private String getReplyBody(Message msg, String body, int format, boolean isHtml, String fromtitle,
            String totitle, String cctitle, String datetitle, String subjecttitle, ArrayList<String> attnames)
            throws MessagingException {
        UserProfile profile = environment.getProfile();
        Locale locale = profile.getLocale();
        String msgSubject = msg.getSubject();
        if (msgSubject == null) {
            msgSubject = "";
        }
        msgSubject = MailUtils.htmlescape(msgSubject);
        Address ad[] = msg.getFrom();
        String msgFrom = "";
        if (ad != null) {
            msgFrom = isHtml ? getHTMLDecodedAddress(ad[0]) : getDecodedAddress(ad[0]);
        }
        java.util.Date dt = msg.getSentDate();
        String msgDate = "";
        if (dt != null) {
            msgDate = DateFormat.getDateTimeInstance(java.text.DateFormat.LONG, java.text.DateFormat.LONG, locale)
                    .format(dt);
        }
        ad = msg.getRecipients(Message.RecipientType.TO);
        String msgTo = null;
        if (ad != null) {
            msgTo = "";
            for (int j = 0; j < ad.length; ++j) {
                msgTo += isHtml ? getHTMLDecodedAddress(ad[j]) : getDecodedAddress(ad[j]) + " ";
            }
        }
        ad = msg.getRecipients(Message.RecipientType.CC);
        String msgCc = null;
        if (ad != null) {
            msgCc = "";
            for (int j = 0; j < ad.length; ++j) {
                msgCc += isHtml ? getHTMLDecodedAddress(ad[j]) : getDecodedAddress(ad[j]) + " ";
            }
        }

        //
        /*String sfrom = "";
        try {
           if (msg.getFrom() != null) {
        InternetAddress ia = (InternetAddress) msg.getFrom()[0];
        String personal = ia.getPersonal();
        String mail = ia.getAddress();
        if (personal == null || personal.equals(mail)) {
           sfrom = mail;
        } else {
           sfrom = personal + " <" + mail + ">";
        }
           }
        } catch (Exception exc) {
               
        }*/
        StringBuffer sb = new StringBuffer();
        String cr = "\n";
        if (format != SimpleMessage.FORMAT_TEXT) {
            cr = "<BR>";
        }
        if (format != SimpleMessage.FORMAT_HTML) {
            if (format == SimpleMessage.FORMAT_PREFORMATTED) {
                sb.append("<TT>");
            }
            sb.append(cr + cr + cr
                    + "----------------------------------------------------------------------------------" + cr
                    + cr);
            sb.append(fromtitle + ": " + msgFrom + cr);
            if (msgTo != null) {
                sb.append(totitle + ": " + msgTo + cr);
            }
            if (msgCc != null) {
                sb.append(cctitle + ": " + msgCc + cr);
            }
            sb.append(datetitle + ": " + msgDate + cr);
            sb.append(subjecttitle + ": " + msgSubject + cr + cr);
            if (format == SimpleMessage.FORMAT_PREFORMATTED) {
                sb.append("</TT>");
            }
        } else {
            sb.append(cr + "<HR>" + cr + cr);
            sb.append("<font face='Arial, Helvetica, sans-serif' size=2>");
            sb.append("<B>" + fromtitle + ":</B> " + msgFrom + "<BR>");
            if (msgTo != null) {
                sb.append("<B>" + totitle + ":</B> " + msgTo + "<BR>");
            }
            if (msgCc != null) {
                sb.append("<B>" + cctitle + ":</B> " + msgCc + "<BR>");
            }
            sb.append("<B>" + datetitle + ":</B> " + msgDate + "<BR>");
            sb.append("<B>" + subjecttitle + ":</B> " + msgSubject + "<BR>");
            sb.append("</font><br>" + cr);
        }

        // Prepend "> " for each line in the body
        //
        if (body != null) {
            if (format == SimpleMessage.FORMAT_HTML) {
                //        sb.append("<TABLE border=0 width='100%'><TR><td width=2 bgcolor=#000088></td><td width=2></td><td>");
                sb.append(
                        "<BLOCKQUOTE style='BORDER-LEFT: #000080 2px solid; MARGIN-LEFT: 5px; PADDING-LEFT: 5px'>");
            }
            if (!isHtml) {
                if (format == SimpleMessage.FORMAT_PREFORMATTED) {
                    sb.append(
                            "<BLOCKQUOTE style='BORDER-LEFT: #000080 2px solid; MARGIN-LEFT: 5px; PADDING-LEFT: 5px'>");
                    sb.append("<tt>");
                }
                StringTokenizer st = new StringTokenizer(body, "\n", true);
                while (st.hasMoreTokens()) {
                    String token = st.nextToken();
                    if (token.equals("\n")) {
                        sb.append(cr);
                    } else {
                        if (format == SimpleMessage.FORMAT_TEXT) {
                            sb.append("> ");
                        }
                        //sb.append(MailUtils.htmlescape(token));
                        sb.append(token);
                    }
                }
                if (format == SimpleMessage.FORMAT_PREFORMATTED) {
                    sb.append("</tt>");
                    sb.append("</BLOCKQUOTE>");
                }
            } else {
                /*
                //String ubody = body.toUpperCase();
                while (true) {
                   int ix1 = StringUtils.indexOfIgnoreCase(body,"<BODY");
                   if (ix1 < 0) {
                      break;
                   }
                   int ix2 = StringUtils.indexOfIgnoreCase(body,">", ix1 + 1);
                   if (ix2 < 0) {
                      ix2 = ix1 + 4;
                   }
                   int ix3 = StringUtils.indexOfIgnoreCase(body,"</BODY", ix2 + 1);
                   if (ix3 > 0) {
                      body = body.substring(ix2 + 1, ix3);
                   } else {
                      body = body.substring(ix2 + 1);
                   }
                }
                //body=removeStartEndTag(body,unwantedTags);
                */

                sb.append(body);
            }
            htmlAppendAttachmentNames(sb, attnames);
            if (format == SimpleMessage.FORMAT_HTML) {
                //        sb.append("</td></tr></table>");
                sb.append("</BLOCKQUOTE>");
            }
        }
        return sb.toString();
    }

    private void removeDestination(Message msg, String email) throws MessagingException {
        Address a[] = null;
        try {
            a = msg.getRecipients(Message.RecipientType.TO);
        } catch (AddressException exc) {

        }
        if (a != null) {
            msg.setRecipients(Message.RecipientType.TO, removeDestination(a, email));
        }
        try {
            a = msg.getRecipients(Message.RecipientType.CC);
        } catch (AddressException exc) {

        }
        if (a != null) {
            msg.setRecipients(Message.RecipientType.CC, removeDestination(a, email));
        }
        try {
            a = msg.getRecipients(Message.RecipientType.BCC);
        } catch (AddressException exc) {

        }
        if (a != null) {
            msg.setRecipients(Message.RecipientType.BCC, removeDestination(a, email));
        }
    }

    private Address[] removeDestination(Address a[], String email) throws MessagingException {
        email = email.toLowerCase();
        Vector v = new Vector();
        for (int i = 0; i < a.length; ++i) {
            InternetAddress ia = (InternetAddress) a[i];
            if (!ia.getAddress().toLowerCase().equals(email)) {
                v.addElement(a[i]);
            }
        }
        Address na[] = new Address[v.size()];
        v.copyInto(na);
        return na;
    }

    private void textAppendAttachmentNames(StringBuffer sb, ArrayList<String> attnames) {
        if (attnames.size() > 0) {
            sb.append("\n\n");
            for (String name : attnames) {
                sb.append("<<" + name + ">>\n");
            }
        }
    }

    private void htmlAppendAttachmentNames(StringBuffer sb, ArrayList<String> attnames) {
        if (attnames.size() > 0) {
            sb.append("<br><br>");
            for (String name : attnames) {
                sb.append("&lt;&lt;" + name + "&gt;&gt;<br>");
            }
        }
    }

    private String getTextContentAsString(Part p) throws IOException, MessagingException {
        java.io.InputStream istream = null;
        String charset = MailUtils.getCharsetOrDefault(p.getContentType());
        if (!java.nio.charset.Charset.isSupported(charset)) {
            charset = "ISO-8859-1";
        }
        try {
            if (p instanceof javax.mail.internet.MimeMessage) {
                javax.mail.internet.MimeMessage mm = (javax.mail.internet.MimeMessage) p;
                istream = mm.getInputStream();
            } else if (p instanceof javax.mail.internet.MimeBodyPart) {
                javax.mail.internet.MimeBodyPart mm = (javax.mail.internet.MimeBodyPart) p;
                istream = mm.getInputStream();
            }
        } catch (Exception exc) { //unhandled format, get Raw data
            if (p instanceof javax.mail.internet.MimeMessage) {
                javax.mail.internet.MimeMessage mm = (javax.mail.internet.MimeMessage) p;
                istream = mm.getRawInputStream();
            } else if (p instanceof javax.mail.internet.MimeBodyPart) {
                javax.mail.internet.MimeBodyPart mm = (javax.mail.internet.MimeBodyPart) p;
                istream = mm.getRawInputStream();
            }
        }

        if (istream == null) {
            throw new IOException("Unknown message class " + p.getClass().getName());
        }

        java.io.BufferedReader br = new java.io.BufferedReader(new java.io.InputStreamReader(istream, charset));
        String line = null;
        StringBuffer sb = new StringBuffer();
        while ((line = br.readLine()) != null) {
            sb.append(line);
            sb.append("\n");
        }
        br.close();
        return sb.toString();
    }

    private boolean appendReplyParts(Part p, StringBuffer htmlsb, StringBuffer textsb, ArrayList<String> attnames)
            throws MessagingException, IOException {
        boolean isHtml = false;
        String disp = p.getDisposition();
        if (disp != null && disp.equalsIgnoreCase(Part.ATTACHMENT) && !p.isMimeType("message/*")) {
            if (attnames != null) {
                String id[] = p.getHeader("Content-ID");
                if (id == null || id[0] == null) {
                    String filename = p.getFileName();
                    if (filename != null) {
                        if (filename.startsWith("<")) {
                            filename = filename.substring(1);
                        }
                        if (filename.endsWith(">")) {
                            filename = filename.substring(0, filename.length() - 1);
                        }
                    }
                    if (filename != null) {
                        attnames.add(filename);
                    }
                }
            }
            return false;
        }
        if (p.isMimeType("text/html")) {
            //String htmlcontent=(String)p.getContent();
            String htmlcontent = getTextContentAsString(p);
            textsb.append(MailUtils.htmlToText(MailUtils.htmlunescapesource(htmlcontent)));
            htmlsb.append(MailUtils.htmlescapefixsource(/*getBodyInnerHtml(*/htmlcontent/*)*/));
            isHtml = true;
        } else if (p.isMimeType("text/plain")) {
            String content = getTextContentAsString(p);
            textsb.append(content);
            htmlsb.append(startpre + MailUtils.htmlescape(content) + endpre);
            isHtml = false;
        } else if (p.isMimeType("message/delivery-status") || p.isMimeType("message/disposition-notification")) {
            InputStream is = (InputStream) p.getContent();
            char cbuf[] = new char[8000];
            byte bbuf[] = new byte[8000];
            int n = 0;
            htmlsb.append(startpre);
            while ((n = is.read(bbuf)) >= 0) {
                if (n > 0) {
                    for (int i = 0; i < n; ++i) {
                        cbuf[i] = (char) bbuf[i];
                    }
                    textsb.append(cbuf);
                    htmlsb.append(MailUtils.htmlescape(new String(cbuf)));
                }
            }
            htmlsb.append(endpre);
            is.close();
            isHtml = false;
        } else if (p.isMimeType("multipart/alternative")) {
            Multipart mp = (Multipart) p.getContent();
            Part bestPart = null;
            for (int i = 0; i < mp.getCount(); ++i) {
                Part part = mp.getBodyPart(i);
                if (part.isMimeType("multipart/*")) {
                    isHtml = appendReplyParts(part, htmlsb, textsb, attnames);
                    if (isHtml) {
                        bestPart = null;
                        break;
                    }
                } else if (part.isMimeType("text/html")) {
                    bestPart = part;
                    break;
                } else if (bestPart == null && part.isMimeType("text/plain")) {
                    bestPart = part;
                } else if (bestPart == null && part.isMimeType("message/*")) {
                    bestPart = part;
                }
            }
            if (bestPart != null) {
                isHtml = appendReplyParts(bestPart, htmlsb, textsb, attnames);
            }
        } else if (p.isMimeType("multipart/*")) {
            Multipart mp = (Multipart) p.getContent();
            for (int i = 0; i < mp.getCount(); ++i) {
                if (appendReplyParts(mp.getBodyPart(i), htmlsb, textsb, attnames)) {
                    isHtml = true;
                }
            }
        } else if (p.isMimeType("message/*")) {
            Object content = p.getContent();
            if (appendReplyParts((MimeMessage) content, htmlsb, textsb, attnames)) {
                isHtml = true;
            }
        } else {
        }
        textsb.append('\n');
        textsb.append('\n');

        return isHtml;
    }

    protected void finalize() {
        cleanup();
    }

    public void cleanup() {
        if (mft != null) {
            mft.abort();
        }
        if (ast != null && ast.isRunning()) {
            ast.cancel();
        }

        for (MailAccount account : accounts.values()) {
            account.cleanup();
        }
        logger.trace("exiting cleanup");
    }

    protected void clearAllCloudAttachments() {
        msgcloudattach.clear();
    }

    private String getMessageID(Message m) throws MessagingException {
        String ids[] = m.getHeader("Message-ID");
        if (ids == null) {
            return null;
        }
        return ids[0];
    }

    private int[] toInts(String values[]) {
        int ret[] = new int[values.length];
        for (int i = 0; i < values.length; ++i) {
            ret[i] = Integer.parseInt(values[i]);
        }
        return ret;
    }

    private long[] toLongs(String values[]) {
        long ret[] = new long[values.length];
        for (int i = 0; i < values.length; ++i)
            ret[i] = Long.parseLong(values[i]);
        return ret;
    }

    public boolean hasDmsDocumentArchiving() {
        return RunContext.isPermitted(true, SERVICE_ID, "DMS_DOCUMENT_ARCHIVING");
    }

    public String getDmsSimpleArchivingMailFolder() {
        return us.getSimpleDMSArchivingMailFolder();
    }

    public boolean isDmsSimpleArchiving() {
        return us.getDMSMethod().equals(MailSettings.ARCHIVING_DMS_METHOD_SIMPLE);
    }

    public boolean isDmsStructuredArchiving() {
        return us.getDMSMethod().equals(MailSettings.ARCHIVING_DMS_METHOD_STRUCTURED);
    }

    public boolean isDmsWebtopArchiving() {
        return us.getDMSMethod().equals(MailSettings.ARCHIVING_DMS_METHOD_WEBTOP);
    }

    public boolean isDmsFolder(MailAccount account, String foldername) {
        if (!hasDmsDocumentArchiving()) {
            return false;
        }
        boolean b = false;
        String df = us.getSimpleDMSArchivingMailFolder();
        if (df != null && df.trim().length() > 0) {
            String lfn = account.getLastFolderName(foldername);
            String dfn = account.getLastFolderName(df);
            if (lfn.equals(dfn)) {
                b = true;
            }
        }
        return b;
    }

    public String getDecodedAddress(Address a) {
        String ret = "";
        try {
            InternetAddress ia = (InternetAddress) a;
            String personal = ia.getPersonal();
            String email = ia.getAddress();
            if (personal == null || personal.equals(email)) {
                ret = email;
            } else {
                ret = personal + " <" + email + ">";
            }
        } catch (RuntimeException exc) {
            ret = a.toString();
        }
        return ret;
    }

    public String getHTMLDecodedAddress(Address a) {
        String s = getDecodedAddress(a);
        return MailUtils.htmlescape(s);
    }

    public String getInternationalFolderName(FolderCache fc) {
        Folder folder = fc.getFolder();
        String desc = folder.getName();
        String fullname = folder.getFullName();
        //WebTopApp webtopapp=environment.getWebTopApp();
        Locale locale = environment.getProfile().getLocale();
        if (fc.isInbox()) {
            desc = lookupResource(MailLocaleKey.FOLDERS_INBOX);
        } else if (fc.isSharedFolder()) {
            desc = lookupResource(MailLocaleKey.FOLDERS_SHARED);
        } else if (fc.isDrafts()) {
            desc = lookupResource(MailLocaleKey.FOLDERS_DRAFTS);
        } else if (fc.isTrash()) {
            desc = lookupResource(MailLocaleKey.FOLDERS_TRASH);
        } else if (fc.isArchive()) {
            desc = lookupResource(MailLocaleKey.FOLDERS_ARCHIVE);
        } else if (fc.isSent()) {
            desc = lookupResource(MailLocaleKey.FOLDERS_SENT);
        } else if (fc.isSpam()) {
            desc = lookupResource(MailLocaleKey.FOLDERS_SPAM);
        }
        return desc;
    }

    public String getInternationalFolderPath(FolderCache fc) throws MessagingException {
        String intpath = getInternationalFolderName(fc);
        char sep = fc.getAccount().getFolderSeparator();
        FolderCache parent = fc.getParent();
        while (parent != null && parent.isRoot()) {
            String intparent = getInternationalFolderName(parent);
            intpath = intparent + sep + intpath;
            parent = parent.getParent();
        }
        return intpath;
    }

    public static int getPriority(Message m) throws MessagingException {
        String xprio = null;
        String h[] = m.getHeader("X-Priority");
        if (h != null && h.length > 0) {
            xprio = h[0];
        }
        int priority = 3;
        if (xprio != null) {
            int ixp = xprio.indexOf(' ');
            if (ixp > 0) {
                xprio = xprio.substring(0, ixp);
            }
            try {
                priority = Integer.parseInt(xprio);
            } catch (RuntimeException exc) {
            }
        }
        return priority;
    }

    public boolean isInlineableMime(String contenttype) {
        return inlineableMimes.contains(contenttype.toLowerCase());
    }

    public synchronized int getNewMessageID() {
        return ++newMessageID;
    }

    public ArrayList<CloudAttachment> getCloudAttachments(long msgid) {
        ArrayList<CloudAttachment> attachments = msgcloudattach.get(new Long(msgid));
        if (attachments != null) {
            return attachments;
        }
        return emptyAttachments;
    }

    public CloudAttachment getCloudAttachment(long msgid, String filename) {
        ArrayList<CloudAttachment> attachments = getCloudAttachments(msgid);
        for (CloudAttachment att : attachments) {
            if (att.getName().equals(filename)) {
                return att;
            }
        }
        return null;
    }

    public void clearCloudAttachments(long msgid) {
        msgcloudattach.remove(msgid);
    }

    public void deleteCloudAttachments(long msgid) {
        ArrayList<CloudAttachment> attachments = getCloudAttachments(msgid);
        for (CloudAttachment a : attachments) {
            try {
                vfsmanager.deleteStoreFile(a.getStoreId(), a.getPath());
            } catch (Exception exc) {
                exc.printStackTrace();
            }
        }
        clearCloudAttachments(msgid);
    }

    public void putCloudAttachments(long msgid, ArrayList<CloudAttachment> attachments) {
        msgcloudattach.put(new Long(msgid), attachments);
    }

    public CloudAttachment attachCloud(long msgid, int storeId, String path, String name) {
        CloudAttachment attachment = null;
        ArrayList<CloudAttachment> attachments = getCloudAttachments(msgid);
        if (attachments == null || attachments == emptyAttachments) {
            attachments = new ArrayList<CloudAttachment>();
            putCloudAttachments(msgid, attachments);
        }
        attachment = new CloudAttachment(storeId, path, name);
        attachments.add(attachment);
        return attachment;
    }

    public String replaceCidUrls(String html, HTMLMailData maildata, String preurl) throws MessagingException {
        for (String cidname : maildata.getCidNames()) {
            //Part part=maildata.getCidPart(cidname);
            String surl = preurl + cidname;
            html = StringUtils.replace(html, "cid:" + cidname, surl);
        }
        return html;
    }

    public String removeWrongBase(String html) {
        String lhtml = html.toLowerCase();
        int ix = lhtml.indexOf("<base");
        if (ix >= 0) {
            int iy = lhtml.indexOf(">", ix);
            if (iy >= 0) {
                String base = lhtml.substring(ix, iy);
                if (base.indexOf("file:") >= 0) {
                    String html1 = html.substring(0, ix);
                    String html2 = html.substring(iy + 1);
                    html = html1 + html2;
                }
            }
        }
        return html;
    }

    private ArrayList<FolderCache> opened = new ArrayList<FolderCache>();

    protected void poolOpened(FolderCache fc) {

        if (opened.size() >= 5) {
            FolderCache rfc = opened.remove(0);
            rfc.cleanup(false);
            rfc.close();
            rfc.setForceRefresh();
        }
        opened.add(fc);
    }

    private MailAccount getAccount(HttpServletRequest request) {
        String account = request.getParameter("account");
        if (account != null)
            return accounts.get(account);
        return mainAccount;
    }

    private MailAccount getAccount(String account) {
        return accounts.get(account);
    }

    //Client service requests
    public void processGetImapTree(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {

        String pfoldername = request.getParameter("node");
        MailAccount account = getAccount(request);
        Folder folder = null;
        try {
            boolean connected = account.checkStoreConnected();
            if (!connected)
                throw new Exception("Mail account authentication error");

            boolean isroot = pfoldername.equals("/");
            if (isroot)
                folder = account.getDefaultFolder();
            else
                folder = account.getFolder(pfoldername);

            Folder folders[] = folder.list();
            String fprefix = account.getFolderPrefix();
            boolean level1 = (fprefix != null /*&& fprefix.equals("INBOX.")*/ && fprefix.trim().length() > 0);
            ArrayList<JsFolder> jsFolders = new ArrayList<>();
            //out.print("{ data:[");
            if (isroot && account.hasDifferentDefaultFolder()) {
                Folder fcs[] = new Folder[0];
                //check for other shared folders to be added
                Folder rfolders[] = account.getRealDefaultFolder().list();
                ArrayList<Folder> afcs = new ArrayList<Folder>();
                for (String sharedPrefix : account.getSharedPrefixes()) {
                    for (Folder rfolder : rfolders) {
                        if (rfolder.getFullName().equals(sharedPrefix))
                            afcs.add(rfolder);
                    }
                }
                //don't mind about just the Shared folder with no child (size=1)
                if (afcs.size() > 0)
                    fcs = afcs.toArray(fcs);

                Folder xfolders[] = new Folder[1 + folders.length + fcs.length];
                xfolders[0] = folder;
                System.arraycopy(folders, 0, xfolders, 1, folders.length);
                if (fcs.length > 0)
                    System.arraycopy(fcs, 0, xfolders, 1 + folders.length, fcs.length);
                folders = xfolders;
            }
            outputFolders(account, folder, folders, level1, false, jsFolders);
            new JsonResult("data", jsFolders).printTo(out, false);
            //out.println("], message: '' }");

        } catch (Exception exc) {
            new JsonResult(exc).printTo(out);
            Service.logger.error("Exception", exc);
        }
    }

    private void outputFolders(MailAccount account, Folder parent, Folder folders[], boolean level1,
            boolean favorites, ArrayList<JsFolder> jsFolders) throws Exception {
        boolean hasPrefix = !StringUtils.isBlank(account.getFolderPrefix());
        String prefixMatch = StringUtils.stripEnd(account.getFolderPrefix(), account.getFolderSeparator() + "");
        ArrayList<Folder> postPrefixList = new ArrayList<Folder>();
        ArrayList<Folder> afolders;
        if (!favorites)
            afolders = sortFolders(account, folders);
        else {
            afolders = new ArrayList<Folder>();
            for (Folder f : folders) {
                if (f != null) {
                    afolders.add(f);
                }
            }
        }
        //If Shared Folders, sort on description
        if (parent != null && account.isSharedFolder(parent.getFullName())) {
            if (!account.hasDifferentDefaultFolder() || !account.isDefaultFolder(parent.getFullName())) {
                String ss = mprofile.getSharedSort();
                if (ss.equals("D")) {
                    Collections.sort(afolders, account.getDescriptionFolderComparator());
                } else if (ss.equals("U")) {
                    Collections.sort(afolders, account.getWebTopUserFolderComparator());
                }
            }
        }

        //If at level 1, look for the prefix folder in the list
        Folder prefixFolder = null;
        if (level1) {
            for (Folder f : afolders) {
                if (f.getFullName().equals(prefixMatch)) {
                    prefixFolder = f;
                    break;
                }
            }
            //remove it and use it later
            if (prefixFolder != null)
                afolders.remove(prefixFolder);
        }

        //now scan and output folders
        for (Folder f : afolders) {
            String foldername = f.getFullName();
            //in case of moved root, check not to duplicate root elsewhere
            if (account.hasDifferentDefaultFolder()) {
                if (account.isDovecot()) {
                    if (account.isDefaultFolder(foldername))
                        continue;
                } else {
                    //skip default folder under shared
                    if (account.isDefaultFolder(foldername) && parent != null
                            && !parent.getFullName().equals(foldername))
                        continue;
                }
            }

            //skip hidden
            if (us.isFolderHidden(foldername))
                continue;

            FolderCache mc = account.getFolderCache(foldername);
            if (mc == null && parent != null) {
                //continue;
                //System.out.println("foldername="+foldername+" parentname="+parent.getFullName());
                FolderCache fcparent = account.getFolderCache(parent.getFullName());
                mc = account.addSingleFoldersCache(fcparent, f);
            }
            //String shortfoldername=getShortFolderName(foldername);
            IMAPFolder imapf = (IMAPFolder) f;
            String atts[] = imapf.getAttributes();
            boolean leaf = true;
            boolean noinferiors = false;
            if (account.hasDifferentDefaultFolder() && account.isDefaultFolder(foldername)) {

            } else if (!favorites) {
                for (String att : atts) {
                    if (att.equals("\\HasChildren")) {
                        if (!level1 || !foldername.equals(account.getInboxFolderFullName()))
                            leaf = false;
                    } else if (att.equals("\\Noinferiors"))
                        noinferiors = true;
                }
                if (noinferiors)
                    leaf = true;
            }
            //boolean leaf=isLeaf((IMAPFolder)f);
            //logger.debug("folder {} isleaf={}, level1={}",f.getFullName(),leaf,level1);
            //if (leaf) {
            //   if (!level1 || !foldername.equals("INBOX")) leaf=false;
            //}

            if (!favorites && prefixFolder != null && !foldername.equals("INBOX")
                    && !foldername.startsWith(account.getFolderPrefix())) {
                postPrefixList.add(f);
            } else {
                /*            
                            String iconCls = "wtmail-icon-imap-folder-xs";
                            int unread = 0;
                            boolean hasUnread = false;
                            boolean nounread = false;
                            if (mc.isSharedFolder()) {
                               iconCls = "wtmail-icon-shared-folder-xs";
                               nounread = true;
                            } else if (mc.isInbox()) {
                               iconCls = "wtmail-icon-inbox-folder-xs";
                            } else if (mc.isSent()) {
                               iconCls = "wtmail-icon-sent-folder-xs";
                               nounread = true;
                            } else if (mc.isDrafts()) {
                               iconCls = "wtmail-icon-drafts-folder-xs";
                               nounread = true;
                            } else if (mc.isTrash()) {
                               iconCls = "wtmail-icon-trash-folder-xs";
                               nounread = true;
                            } else if (mc.isArchive()) {
                               iconCls = "wtmail-icon-archive-folder-xs";
                               nounread = true;
                            } else if (mc.isSpam()) {
                               iconCls = "wtmail-icon-spam-folder-xs";
                               nounread = true;
                            } else if (mc.isDms()) {
                               iconCls = "wtmail-icon-dms-folder-xs";
                            } else if (mc.isSharedInbox()) {
                               iconCls = "wtmail-icon-inbox-folder-xs";
                            }
                            if (!nounread) {
                               unread = mc.getUnreadMessagesCount();
                               hasUnread = mc.hasUnreadChildren();
                            }
                            String text = mc.getDescription();
                            String group = us.getMessageListGroup(foldername);
                            if (group == null) {
                               group = "";
                            }
                    
                            String ss = "{id:'" + StringEscapeUtils.escapeEcmaScript(foldername)
                                  + "',text:'" + StringEscapeUtils.escapeEcmaScript(description)
                                  + "',folder:'" + StringEscapeUtils.escapeEcmaScript(text)
                                  + "',leaf:" + leaf
                                  + ",iconCls: '" + iconCls
                                  + "',unread:" + unread
                                  + ",hasUnread:" + hasUnread
                                  + ",group: '"+group+"'";
                    
                            boolean isSharedToSomeone=false;
                            try {
                               isSharedToSomeone=mc.isSharedToSomeone();
                            } catch(Exception exc) {
                    
                            }
                            if (isSharedToSomeone) ss+=",isSharedToSomeone: true";
                            if (mc.isSharedFolder()) ss+=",isSharedRoot: true";
                            if (account.isUnderSharedFolder(foldername)) ss+=",isUnderShared: true";
                            if (mc.isInbox()) {
                               ss += ",isInbox: true";
                            }
                            if (mc.isSent()) {
                               ss += ",isSent: true";
                            }
                            if (account.isUnderSentFolder(mc.getFolderName())) {
                               ss += ",isUnderSent: true";
                            }
                            if (mc.isDrafts()) {
                               ss += ",isDrafts: true";
                            }
                            if (mc.isTrash()) {
                               ss += ",isTrash: true";
                            }
                            if (mc.isArchive()) {
                               ss += ",isArchive: true";
                            }
                            if (mc.isSpam()) {
                               ss += ",isSpam: true";
                            }
                            if (mc.isScanForcedOff()) {
                               ss += ", scanOff: true";
                            } else if (mc.isScanForcedOn()) {
                               ss += ", scanOn: true";
                            } else if (mc.isScanEnabled()) {
                               ss += ", scanEnabled: true";
                            }
                    
                            boolean canRename=true;
                            if (mc.isInbox() || mc.isSpecial() || mc.isSharedFolder() || (mc.getParent()!=null && mc.getParent().isSharedFolder())) canRename=false;
                            ss += ", canRename: "+canRename;
                    
                            ss += ", account: '"+account.getId()+"'";
                            ss += "},";
                    
                            out.print(ss);
                */
                jsFolders.add(createJsFolder(mc, leaf));
            }
        }

        //if we have a prefix folder output remaining folders
        if (!favorites && prefixFolder != null) {
            for (Folder ff : prefixFolder.list())
                postPrefixList.add(ff);
            ArrayList<Folder> sortedFolders = sortFolders(account,
                    postPrefixList.toArray(new Folder[postPrefixList.size()]));
            outputFolders(account, prefixFolder, sortedFolders.toArray(new Folder[sortedFolders.size()]), false,
                    false, jsFolders);
        }
    }

    private JsFolder createJsFolder(FolderCache fc, boolean leaf) {
        String foldername = fc.getFolderName();
        MailAccount account = fc.getAccount();
        String iconCls = "wtmail-icon-imap-folder-xs";
        int unread = 0;
        boolean hasUnread = false;
        boolean nounread = false;
        if (fc.isSharedFolder()) {
            iconCls = "wtmail-icon-shared-folder-xs";
            nounread = true;
        } else if (fc.isInbox()) {
            iconCls = "wtmail-icon-inbox-folder-xs";
        } else if (fc.isSent()) {
            iconCls = "wtmail-icon-sent-folder-xs";
            nounread = true;
        } else if (fc.isDrafts()) {
            iconCls = "wtmail-icon-drafts-folder-xs";
            nounread = true;
        } else if (fc.isTrash()) {
            iconCls = "wtmail-icon-trash-folder-xs";
            nounread = true;
        } else if (fc.isArchive()) {
            iconCls = "wtmail-icon-archive-folder-xs";
            nounread = true;
        } else if (fc.isSpam()) {
            iconCls = "wtmail-icon-spam-folder-xs";
            nounread = true;
        } else if (fc.isDms()) {
            iconCls = "wtmail-icon-dms-folder-xs";
        } else if (fc.isSharedInbox()) {
            iconCls = "wtmail-icon-inbox-folder-xs";
        }
        if (!nounread) {
            unread = fc.getUnreadMessagesCount();
            hasUnread = fc.hasUnreadChildren();
        }
        String text = fc.getDescription();
        String group = us.getMessageListGroup(foldername);
        if (group == null) {
            group = "";
        }

        JsFolder jsFolder = new JsFolder();
        jsFolder.id = foldername;
        jsFolder.text = text;
        jsFolder.folder = text;
        jsFolder.leaf = leaf;
        jsFolder.iconCls = iconCls;
        jsFolder.unread = unread;
        jsFolder.hasUnread = hasUnread;
        jsFolder.group = group;

        boolean isSharedToSomeone = false;
        try {
            isSharedToSomeone = fc.isSharedToSomeone();
        } catch (Exception exc) {

        }
        if (isSharedToSomeone)
            jsFolder.isSharedToSomeone = true;
        if (fc.isSharedFolder())
            jsFolder.isSharedRoot = true;
        if (account.isUnderSharedFolder(foldername))
            jsFolder.isUnderShared = true;
        if (fc.isInbox())
            jsFolder.isInbox = true;
        if (fc.isSent())
            jsFolder.isSent = true;
        if (account.isUnderSentFolder(fc.getFolderName()))
            jsFolder.isUnderSent = true;
        if (fc.isDrafts())
            jsFolder.isDrafts = true;
        if (fc.isTrash())
            jsFolder.isTrash = true;
        if (fc.isArchive())
            jsFolder.isArchive = true;
        if (fc.isSpam())
            jsFolder.isSpam = true;

        if (fc.isScanForcedOff())
            jsFolder.scanOff = true;
        else if (fc.isScanForcedOn())
            jsFolder.scanOn = true;
        else if (fc.isScanEnabled())
            jsFolder.scanEnabled = true;

        boolean canRename = true;
        if (fc.isInbox() || fc.isSpecial() || fc.isSharedFolder()
                || (fc.getParent() != null && fc.getParent().isSharedFolder()))
            canRename = false;
        jsFolder.canRename = canRename;

        jsFolder.account = account.getId();

        return jsFolder;
    }

    protected ArrayList<Folder> sortFolders(MailAccount account, Folder folders[]) {
        ArrayList<Folder> afolders = new ArrayList<Folder>();
        ArrayList<Folder> sfolders = new ArrayList<Folder>();
        HashMap<String, Folder> mfolders = new HashMap<String, Folder>();
        //add all non special fo the array and map special ones for later insert
        Folder inbox = null;
        Folder sent = null;
        Folder drafts = null;
        Folder trash = null;
        Folder archive = null;
        Folder spam = null;
        for (Folder f : folders) {
            String foldername = f.getFullName();
            String shortfoldername = account.getShortFolderName(foldername);
            if (!mfolders.containsKey(shortfoldername)) {
                mfolders.put(shortfoldername, f);
                if (account.isInboxFolder(shortfoldername))
                    inbox = f;
                else if (account.isSentFolder(shortfoldername))
                    sent = f;
                else if (account.isDraftsFolder(shortfoldername))
                    drafts = f;
                else if (account.isTrashFolder(shortfoldername))
                    trash = f;
                else if (account.isSpamFolder(shortfoldername))
                    spam = f;
                else if (account.isArchiveFolder(shortfoldername))
                    archive = f;
                else if (account.isSharedFolder(foldername))
                    sfolders.add(f);
                else
                    afolders.add(f);
            }
        }

        if (sortfolders) {
            Collections.sort(afolders, new Comparator<Folder>() {
                @Override
                public int compare(Folder f1, Folder f2) {
                    return f1.getFullName().toLowerCase().compareTo(f2.getFullName().toLowerCase());
                }
            });
            Collections.sort(sfolders, new Comparator<Folder>() {
                @Override
                public int compare(Folder f1, Folder f2) {
                    return f1.getFullName().toLowerCase().compareTo(f2.getFullName().toLowerCase());
                }
            });
        }

        //add any mapped special folder in order on top
        if (archive != null) {
            afolders.add(0, archive);
        }
        if (trash != null) {
            afolders.add(0, trash);
        }
        if (spam != null) {
            afolders.add(0, spam);
        }
        if (sent != null) {
            afolders.add(0, sent);
        }
        if (drafts != null) {
            afolders.add(0, drafts);
        }
        if (inbox != null) {
            afolders.add(0, inbox);
        }
        //add shared folders at the end
        afolders.addAll(sfolders);

        return afolders;
    }

    public void processShowArchive(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        try {
            boolean connected = archiveAccount.checkStoreConnected();
            if (!connected)
                throw new Exception("Mail account authentication error");
            if (!archiveAccount.hasFolderCache())
                archiveAccount.loadFoldersCache(archiveAccount, false);
            new JsonResult().printTo(out);
        } catch (Exception exc) {
            new JsonResult(exc).printTo(out);
            Service.logger.error("Exception", exc);
        }
    }

    public void processHideArchive(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        archiveAccount.disconnect();
        new JsonResult().printTo(out);
    }

    public void processGetArchiveTree(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        String pfoldername = request.getParameter("node");
        MailAccount account = archiveAccount; //getAccount(request);
        Folder folder = null;
        try {
            boolean connected = account.checkStoreConnected();
            if (!connected)
                throw new Exception("Mail account authentication error");
            if (!account.hasFolderCache())
                account.loadFoldersCache(account, true);

            boolean isroot = pfoldername.equals("/");
            if (isroot)
                folder = account.getDefaultFolder();
            else
                folder = account.getFolder(pfoldername);

            Folder folders[] = folder.list();
            String fprefix = account.getFolderPrefix();
            boolean level1 = (fprefix != null && fprefix.equals("INBOX."));
            ArrayList<JsFolder> jsFolders = new ArrayList<>();
            //out.print("{ data:[");
            if (isroot && account.hasDifferentDefaultFolder()) {
                Folder fcs[] = new Folder[0];
                //check for other shared folders to be added
                Folder rfolders[] = account.getRealDefaultFolder().list();
                ArrayList<Folder> afcs = new ArrayList<Folder>();
                for (String sharedPrefix : account.getSharedPrefixes()) {
                    for (Folder rfolder : rfolders) {
                        if (rfolder.getFullName().equals(sharedPrefix))
                            afcs.add(rfolder);
                    }
                }
                if (afcs.size() > 0)
                    fcs = afcs.toArray(fcs);

                Folder xfolders[] = new Folder[1 + folders.length + fcs.length];
                xfolders[0] = folder;
                System.arraycopy(folders, 0, xfolders, 1, folders.length);
                if (fcs.length > 0)
                    System.arraycopy(fcs, 0, xfolders, 1 + folders.length, fcs.length);
                folders = xfolders;
            }
            outputFolders(account, folder, folders, level1, false, jsFolders);
            new JsonResult("data", jsFolders).printTo(out, false);
            //out.println("], message: '' }");

        } catch (Exception exc) {
            new JsonResult(exc).printTo(out);
            Service.logger.error("Exception", exc);
        }
    }

    class FavoriteFolderData {
        FolderCache folderCache;
        String description;

        FavoriteFolderData(FolderCache fc, String desc) {
            this.folderCache = fc;
            this.description = desc;
        }
    }

    private boolean getFavoritesTreeDone = false;

    public void processGetFavoritesTree(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        //first call runs external checks
        if (!getFavoritesTreeDone) {
            for (MailAccount extacc : externalAccounts) {
                extacc.getFoldersThread().start();
            }
            getFavoritesTreeDone = true;
        }

        try {
            //synchronized(mft) {
            ArrayList<JsFolder> jsFolders = new ArrayList<>();
            //out.print("{ data:[");

            MailUserSettings.FavoriteFolders favorites;
            boolean removeOldSetting = false;
            //use new setting if exists
            if (us.hasFavoriteFolders())
                favorites = us.getFavoriteFolders();
            //convert old setting
            else {
                MailUserSettings.Favorites off = us.getFavorites();
                favorites = new MailUserSettings.FavoriteFolders();
                for (String foldername : off) {
                    favorites.add(new MailUserSettings.FavoriteFolder(MAIN_ACCOUNT_ID, foldername, foldername));
                }
                removeOldSetting = true;
            }
            MailUserSettings.FavoriteFolders newFavorites = new MailUserSettings.FavoriteFolders();
            ArrayList<FavoriteFolderData> ffds = new ArrayList<>();
            for (int i = 0; i < favorites.size(); ++i) {
                MailUserSettings.FavoriteFolder ff = favorites.get(i);
                MailAccount account = getAccount(ff.accountId);
                if (account != null) {
                    Folder folder = account.getFolder(ff.folderId);
                    if (folder.exists()) {
                        FolderCache fc = new FolderCache(account, folder, this, environment);
                        newFavorites.add(ff);
                        ffds.add(new FavoriteFolderData(fc, ff.description));
                    }
                }
            }
            us.setFavoriteFolders(newFavorites);
            //remove old setting if present
            if (removeOldSetting) {
                us.deleteOldFavoritesSetting();
            }

            for (FavoriteFolderData ffd : ffds) {
                JsFolder jsFolder = createJsFolder(ffd.folderCache, true);
                jsFolder.folder = ffd.description;
                jsFolders.add(jsFolder);
            }
            //outputFolders(mainAccount, mainAccount.getDefaultFolder(), folders, false, true, jsFolders);
            new JsonResult("data", jsFolders).printTo(out, false);
            //out.println("], message: '' }");
            //}

        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            new JsonResult(exc).printTo(out);
        }
    }

    public void processAddToFavorites(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        String account = request.getParameter("account");
        String folder = request.getParameter("folder");
        String description = request.getParameter("description");

        MailUserSettings.FavoriteFolders favorites = us.getFavoriteFolders();
        favorites.add(account, folder, description);
        us.setFavoriteFolders(favorites);
        new JsonResult().printTo(out);
    }

    public void processRemoveFavorite(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        String account = request.getParameter("account");
        String folder = request.getParameter("folder");
        MailUserSettings.FavoriteFolders favorites = us.getFavoriteFolders();
        favorites.remove(account, folder);
        us.setFavoriteFolders(favorites);
        new JsonResult().printTo(out);
    }

    public void processMoveMessages(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount fromaccount = getAccount(request.getParameter("fromaccount"));
        String fromfolder = request.getParameter("fromfolder");
        MailAccount toaccount = getAccount(request.getParameter("toaccount"));
        String tofolder = request.getParameter("tofolder");
        String allfiltered = request.getParameter("allfiltered");
        String smultifolder = request.getParameter("multifolder");
        boolean multifolder = smultifolder != null && smultifolder.equals("true");
        String sfullthreads = request.getParameter("fullthreads");
        boolean fullthreads = sfullthreads != null && sfullthreads.equals("true");
        String uids[] = null;
        String sout = null;
        boolean archiving = false;
        try {
            fromaccount.checkStoreConnected();
            toaccount.checkStoreConnected();
            FolderCache mcache = fromaccount.getFolderCache(fromfolder);
            FolderCache tomcache = toaccount.getFolderCache(tofolder);
            String foldertrash = toaccount.getFolderTrash();
            String folderspam = toaccount.getFolderSpam();
            String folderarchive = toaccount.getFolderArchive();

            //check if tofolder is my Spam, and there is spamadm, move there
            if (toaccount.isSpamFolder(tofolder)) {
                String spamadmSpam = ss.getSpamadmSpam();
                if (spamadmSpam != null) {
                    folderspam = spamadmSpam;
                    FolderCache fc = toaccount.getFolderCache(spamadmSpam);
                    if (fc != null)
                        tomcache = fc;
                } else if (toaccount.isUnderSharedFolder(fromfolder)) {
                    String mainfolder = toaccount.getMainSharedFolder(fromfolder);
                    if (mainfolder != null) {
                        folderspam = mainfolder + toaccount.getFolderSeparator()
                                + toaccount.getLastFolderName(folderspam);
                        FolderCache fc = toaccount.getFolderCache(folderspam);
                        if (fc != null)
                            tomcache = fc;
                    }
                }
                tofolder = folderspam;
            }
            //if trashing, check for shared profile trash
            else if (toaccount.isTrashFolder(tofolder)) {
                if (toaccount.isUnderSharedFolder(fromfolder)) {
                    String mainfolder = toaccount.getMainSharedFolder(fromfolder);
                    if (mainfolder != null) {
                        foldertrash = mainfolder + toaccount.getFolderSeparator()
                                + toaccount.getLastFolderName(foldertrash);
                        FolderCache fc = toaccount.getFolderCache(foldertrash);
                        if (fc != null)
                            tomcache = fc;
                    }
                }
                tofolder = foldertrash;
            }
            //if archiving, determine destination folder based on settings and shared profile
            else if (toaccount.isArchiveFolder(tofolder)) {
                if (toaccount.isUnderSharedFolder(fromfolder)) {
                    String mainfolder = toaccount.getMainSharedFolder(fromfolder);
                    if (mainfolder != null) {
                        folderarchive = mainfolder + toaccount.getFolderSeparator()
                                + toaccount.getLastFolderName(folderarchive);
                    }
                }
                tofolder = folderarchive;
                archiving = true;
            }

            if (allfiltered == null) {
                uids = request.getParameterValues("ids");
                boolean norows = false;
                if (uids.length == 1 && uids[0].length() == 0)
                    norows = true;
                if (!norows) {
                    if (!multifolder) {
                        if (archiving)
                            archiveMessages(mcache, folderarchive, toLongs(uids), fullthreads);
                        else
                            moveMessages(mcache, tomcache, toLongs(uids), fullthreads);
                    } else {
                        long iuids[] = new long[1];
                        for (String uid : uids) {
                            int ix = uid.indexOf("|");
                            fromfolder = uid.substring(0, ix);
                            uid = uid.substring(ix + 1);
                            mcache = fromaccount.getFolderCache(fromfolder);
                            iuids[0] = Long.parseLong(uid);
                            if (archiving)
                                archiveMessages(mcache, folderarchive, iuids, fullthreads);
                            else
                                moveMessages(mcache, tomcache, iuids, fullthreads);
                        }
                    }
                }
                long millis = System.currentTimeMillis();
                sout = "{\nresult: true, millis: " + millis + ", tofolder: '"
                        + StringEscapeUtils.escapeEcmaScript(tofolder) + "', archiving: " + archiving + "\n}";
            } else {
                uids = getMessageUIDs(mcache, request);
                if (archiving)
                    archiveMessages(mcache, folderarchive, toLongs(uids), fullthreads);
                else
                    moveMessages(mcache, tomcache, toLongs(uids), fullthreads);
                tomcache.refreshUnreads();
                mcache.setForceRefresh();
                long millis = System.currentTimeMillis();
                sout = "{\nresult: true, unread: " + tomcache.getUnreadMessagesCount() + ", millis: " + millis
                        + ", tofolder: '" + StringEscapeUtils.escapeEcmaScript(tofolder) + "', archiving: "
                        + archiving + "\n}";
            }
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        }
        out.println(sout);
    }

    public void processCopyMessages(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount fromaccount = getAccount(request.getParameter("fromaccount"));
        String fromfolder = request.getParameter("fromfolder");
        MailAccount toaccount = getAccount(request.getParameter("toaccount"));
        String tofolder = request.getParameter("tofolder");
        String allfiltered = request.getParameter("allfiltered");
        String smultifolder = request.getParameter("multifolder");
        boolean multifolder = smultifolder != null && smultifolder.equals("true");
        String sfullthreads = request.getParameter("fullthreads");
        boolean fullthreads = sfullthreads != null && sfullthreads.equals("true");
        String sout = null;
        String uids[] = null;
        try {
            fromaccount.checkStoreConnected();
            toaccount.checkStoreConnected();
            FolderCache mcache = fromaccount.getFolderCache(fromfolder);
            FolderCache tomcache = toaccount.getFolderCache(tofolder);
            if (allfiltered == null) {
                uids = request.getParameterValues("ids");
                if (!multifolder)
                    copyMessages(mcache, tomcache, toLongs(uids), fullthreads);
                else {
                    long iuids[] = new long[1];
                    for (String uid : uids) {
                        int ix = uid.indexOf("|");
                        fromfolder = uid.substring(0, ix);
                        uid = uid.substring(ix + 1);
                        mcache = fromaccount.getFolderCache(fromfolder);
                        iuids[0] = Long.parseLong(uid);
                        copyMessages(mcache, tomcache, iuids, fullthreads);
                    }
                }
                long millis = System.currentTimeMillis();
                sout = "{\nresult: true, millis: " + millis + "\n}";
            } else {
                uids = getMessageUIDs(mcache, request);
                copyMessages(mcache, tomcache, toLongs(uids), fullthreads);
                tomcache.refreshUnreads();
                mcache.setForceRefresh();
                long millis = System.currentTimeMillis();
                sout = "{\nresult: true, unread: " + tomcache.getUnreadMessagesCount() + ", millis: " + millis
                        + "\n}";
            }
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        }
        out.println(sout);
    }

    public void processDmsArchiveMessages(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        MailAccount account = getAccount(request);
        String fromfolder = request.getParameter("fromfolder");
        String tofolder = request.getParameter("tofolder");
        int ix = tofolder.indexOf("|");
        String idcategory = tofolder.substring(0, ix);
        String idsubcategory = tofolder.substring(ix + 1);
        String smultifolder = request.getParameter("multifolder");
        boolean multifolder = smultifolder != null && smultifolder.equals("true");
        String sfullthreads = request.getParameter("fullthreads");
        boolean fullthreads = sfullthreads != null && sfullthreads.equals("true");
        String uids[] = null;
        String sout = null;
        try {
            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(fromfolder);
            uids = request.getParameterValues("ids");
            if (!multifolder)
                dmsArchiveMessages(mcache, toLongs(uids), idcategory, idsubcategory, fullthreads);
            else {
                long iuids[] = new long[1];
                for (String uid : uids) {
                    ix = uid.indexOf("|");
                    fromfolder = uid.substring(0, ix);
                    uid = uid.substring(ix + 1);
                    mcache = account.getFolderCache(fromfolder);
                    iuids[0] = Integer.parseInt(uid);
                    dmsArchiveMessages(mcache, iuids, idcategory, idsubcategory, fullthreads);
                }
            }
            long millis = System.currentTimeMillis();
            sout = "{\nresult: true, millis: " + millis + "\n}";
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        }
        out.println(sout);
    }

    String[] getMessageUIDs(FolderCache mcache, HttpServletRequest request) throws MessagingException, IOException {
        String psortfield = request.getParameter("sort");
        String psortdir = request.getParameter("dir");
        String pthreaded = null; //request.getParameter("threaded");
        if (psortfield == null) {
            psortfield = "date";
        }
        if (psortdir == null) {
            psortdir = "DESC";
        }
        boolean threaded = (pthreaded != null && pthreaded.equals("1"));
        int sortby = 0;
        if (psortfield.equals("messageid")) {
            sortby = MessageComparator.SORT_BY_MSGIDX;
        } else if (psortfield.equals("date")) {
            sortby = MessageComparator.SORT_BY_DATE;
        } else if (psortfield.equals("priority")) {
            sortby = MessageComparator.SORT_BY_PRIORITY;
        } else if (psortfield.equals("to")) {
            sortby = MessageComparator.SORT_BY_RCPT;
        } else if (psortfield.equals("from")) {
            sortby = MessageComparator.SORT_BY_SENDER;
        } else if (psortfield.equals("size")) {
            sortby = MessageComparator.SORT_BY_SIZE;
        } else if (psortfield.equals("subject")) {
            sortby = MessageComparator.SORT_BY_SUBJECT;
        } else if (psortfield.equals("status") || psortfield.equals("unread")) {
            sortby = MessageComparator.SORT_BY_STATUS;
        } else if (psortfield.equals("flag")) {
            sortby = MessageComparator.SORT_BY_FLAG;
        }
        boolean ascending = psortdir.equals("ASC");

        String group = us.getMessageListGroup(mcache.getFolderName());
        if (group == null) {
            group = "";
        }

        int sort_group = 0;
        boolean groupascending = true;
        if (group.equals("messageid")) {
            sort_group = MessageComparator.SORT_BY_MSGIDX;
        } else if (group.equals("gdate")) {
            sort_group = MessageComparator.SORT_BY_DATE;
            groupascending = false;
        } else if (group.equals("priority")) {
            sort_group = MessageComparator.SORT_BY_PRIORITY;
        } else if (group.equals("to")) {
            sort_group = MessageComparator.SORT_BY_RCPT;
        } else if (group.equals("from")) {
            sort_group = MessageComparator.SORT_BY_SENDER;
        } else if (group.equals("size")) {
            sort_group = MessageComparator.SORT_BY_SIZE;
        } else if (group.equals("subject")) {
            sort_group = MessageComparator.SORT_BY_SUBJECT;
        } else if (group.equals("status")) {
            sort_group = MessageComparator.SORT_BY_STATUS;
        } else if (group.equals("flag")) {
            sort_group = MessageComparator.SORT_BY_FLAG;
        }

        Message msgs[] = mcache.getMessages(sortby, ascending, false, sort_group, groupascending, threaded, null,
                false);
        ArrayList<String> aids = new ArrayList<String>();
        for (Message m : msgs) {
            aids.add("" + mcache.getUID(m));
            /*            String xids[];
             try {
             xids=m.getHeader("Message-ID");
             } catch(MessagingException exc) {
             continue;
             }
             if (xids!=null && xids.length>0) aids.add(xids[0]);*/
        }
        String uids[] = new String[aids.size()];
        aids.toArray(uids);
        return uids;
    }

    public void processDeleteMessages(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String fromfolder = request.getParameter("fromfolder");
        String allfiltered = request.getParameter("allfiltered");
        String smultifolder = request.getParameter("multifolder");
        boolean multifolder = smultifolder != null && smultifolder.equals("true");
        String sfullthreads = request.getParameter("fullthreads");
        boolean fullthreads = sfullthreads != null && sfullthreads.equals("true");
        String sout = null;
        String uids[];
        try {
            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(fromfolder);

            if (allfiltered == null) {
                uids = request.getParameterValues("ids");
                if (!multifolder) {
                    deleteMessages(mcache, toLongs(uids), fullthreads);
                } else {
                    long iuids[] = new long[1];
                    for (String uid : uids) {
                        int ix = uid.indexOf("|");
                        fromfolder = uid.substring(0, ix);
                        uid = uid.substring(ix + 1);
                        mcache = account.getFolderCache(fromfolder);
                        iuids[0] = Long.parseLong(uid);
                        deleteMessages(mcache, iuids, fullthreads);
                    }
                }
                sout = "{\nresult: true\n}";
            } else {
                uids = getMessageUIDs(mcache, request);
                deleteMessages(mcache, toLongs(uids), true);
                sout = "{\nresult: true\n}";
            }
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        }
        out.println(sout);
    }

    public void processFlagMessages(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String fromfolder = request.getParameter("fromfolder");
        String uids[] = request.getParameterValues("ids");
        String flag = request.getParameter("flag");
        String multifolder = request.getParameter("multifolder");
        boolean mf = multifolder != null && multifolder.equals("true");

        try {
            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(fromfolder);
            if (!mf) {
                if (flag.equals("clear")) {
                    clearMessagesFlag(mcache, toLongs(uids));
                } else {
                    flagMessages(mcache, toLongs(uids), flag);
                }
            } else {
                long iuids[] = new long[1];
                for (String uid : uids) {
                    int ix = uid.indexOf("|");
                    fromfolder = uid.substring(0, ix);
                    uid = uid.substring(ix + 1);
                    mcache = account.getFolderCache(fromfolder);
                    iuids[0] = Long.parseLong(uid);
                    if (flag.equals("clear"))
                        clearMessagesFlag(mcache, iuids);
                    else
                        flagMessages(mcache, iuids, flag);
                }
            }
            new JsonResult().printTo(out);
        } catch (MessagingException exc) {
            Service.logger.error("Exception", exc);
            new JsonResult(false, lookupResource(MailLocaleKey.PERMISSION_DENIED)).printTo(out);
        }
    }

    public void processSeenMessages(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String fromfolder = request.getParameter("fromfolder");
        String uids[] = request.getParameterValues("ids");
        String multifolder = request.getParameter("multifolder");
        boolean mf = multifolder != null && multifolder.equals("true");
        String sout = null;
        try {
            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(fromfolder);
            if (!mf) {
                setMessagesSeen(mcache, toLongs(uids));
            } else {
                long iuids[] = new long[1];
                for (String uid : uids) {
                    int ix = uid.indexOf("|");
                    fromfolder = uid.substring(0, ix);
                    uid = uid.substring(ix + 1);
                    mcache = account.getFolderCache(fromfolder);
                    iuids[0] = Long.parseLong(uid);
                    setMessagesSeen(mcache, iuids);
                }
            }
            long millis = System.currentTimeMillis();
            sout = "{\nresult: true, millis: " + millis + "\n}";
        } catch (MessagingException exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        }
        out.println(sout);
    }

    public void processUnseenMessages(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String fromfolder = request.getParameter("fromfolder");
        String uids[] = request.getParameterValues("ids");
        String multifolder = request.getParameter("multifolder");
        boolean mf = multifolder != null && multifolder.equals("true");
        String sout = null;
        try {
            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(fromfolder);
            if (!mf) {
                setMessagesUnseen(mcache, toLongs(uids));
            } else {
                long iuids[] = new long[1];
                for (String uid : uids) {
                    int ix = uid.indexOf("|");
                    fromfolder = uid.substring(0, ix);
                    uid = uid.substring(ix + 1);
                    mcache = account.getFolderCache(fromfolder);
                    iuids[0] = Long.parseLong(uid);
                    setMessagesUnseen(mcache, iuids);
                }
            }
            long millis = System.currentTimeMillis();
            sout = "{\nresult: true, millis: " + millis + "\n}";
        } catch (MessagingException exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        }
        out.println(sout);
    }

    public void processManageHiddenFolders(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        try {
            String crud = ServletUtils.getStringParameter(request, "crud", true);
            if (crud.equals(Crud.READ)) {
                ArrayList<JsHiddenFolder> hfolders = new ArrayList<>();
                for (String folderId : us.getHiddenFolders()) {
                    hfolders.add(new JsHiddenFolder(folderId, folderId));
                }
                new JsonResult(hfolders).printTo(out);
            } else if (crud.equals(Crud.DELETE)) {
                ServletUtils.StringArray ids = ServletUtils.getObjectParameter(request, "ids",
                        ServletUtils.StringArray.class, true);

                for (String id : ids) {
                    us.setFolderHidden(id, false);
                }
                new JsonResult().printTo(out);
            }
        } catch (Exception exc) {
            logger.debug("Cannot restore hidden folders", exc);
            new JsonResult("Cannot restore hidden folders", exc).printTo(out);
        }
    }

    public void processSetScanFolder(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String folder = request.getParameter("folder");
        boolean value = false;
        String svalue = request.getParameter("value");
        if (svalue != null) {
            value = svalue.equals("1");
        }
        boolean recursive = false;
        String srecursive = request.getParameter("recursive");
        if (srecursive != null) {
            recursive = srecursive.equals("1");
        }
        String sout = null;
        Connection con = null;
        try {
            con = getConnection();
            FolderCache fc = account.getFolderCache(folder);
            setScanFolder(con, fc, value, recursive);
            sout = "{\nresult: true\n}";
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        } finally {
            if (con != null) {
                try {
                    con.close();
                } catch (SQLException exc) {
                }
            }
        }
        out.println(sout);
    }

    private void setScanFolder(Connection con, FolderCache fc, boolean value, boolean recursive)
            throws SQLException {
        UserProfile profile = environment.getProfile();
        String iddomain = profile.getDomainId();
        String login = profile.getUserId();
        String folder = fc.getFolderName();
        if (value) {
            OScan oscan = new OScan(iddomain, login, folder);
            ScanDAO.getInstance().insert(con, oscan);
            fc.setScanEnabled(true);
        } else {
            ScanDAO.getInstance().deleteById(con, iddomain, login, folder);
            fc.setScanEnabled(false);
        }
        if (recursive) {
            ArrayList<FolderCache> children = fc.getChildren();
            if (children != null) {
                for (FolderCache child : children) {
                    setScanFolder(con, child, value, recursive);
                }
            }
        }
    }

    public void processSeenFolder(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String folder = request.getParameter("folder");
        boolean recursive = false;
        String srecursive = request.getParameter("recursive");
        if (srecursive != null) {
            recursive = srecursive.equals("1");
        }
        String sout = null;
        FolderCache mcache = null;
        try {
            account.checkStoreConnected();
            boolean result = true;
            sout = "{\n";
            mcache = account.getFolderCache(folder);
            setMessagesSeen(mcache, true, recursive);
            long millis = System.currentTimeMillis();
            sout += "result: " + result + ", millis: " + millis + "\n}";
        } catch (MessagingException exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, oldid: '" + folder + "', oldname: '"
                    + (mcache != null ? mcache.getFolder().getName() : "unknown") + "', text:'"
                    + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        }
        out.println(sout);
    }

    public void processUnseenFolder(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String folder = request.getParameter("folder");
        boolean recursive = false;
        String srecursive = request.getParameter("recursive");
        if (srecursive != null) {
            recursive = srecursive.equals("1");
        }
        String sout = null;
        FolderCache mcache = null;
        try {
            account.checkStoreConnected();
            boolean result = true;
            sout = "{\n";
            mcache = account.getFolderCache(folder);
            setMessagesSeen(mcache, false, recursive);
            sout += "result: " + result + "\n}";
        } catch (MessagingException exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, oldid: '" + folder + "', oldname: '"
                    + (mcache != null ? mcache.getFolder().getName() : "unknown") + "', text:'"
                    + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        }
        out.println(sout);
    }

    private void setMessagesSeen(FolderCache mcache, boolean seen, boolean recursive) throws MessagingException {
        if (seen) {
            mcache.setMessagesSeen();
        } else {
            mcache.setMessagesUnseen();
        }
        if (recursive) {
            ArrayList<FolderCache> children = mcache.getChildren();
            if (children != null) {
                for (FolderCache fc : children) {
                    setMessagesSeen(fc, seen, recursive);
                }
            }
        }
    }

    public String getPartName(Part p) throws MessagingException {
        String pname = p.getFileName();
        // TODO: Remove code below if already included in JavaMail impl.
        if (pname == null) {
            String hctypes[] = p.getHeader("Content-Type");
            if (hctypes == null || hctypes.length == 0) {
                return null;
            }
            String hctype = hctypes[0];
            int ix = hctype.indexOf("name=");
            if (ix >= 0) {
                int sx = ix + 5;
                int ex = hctype.indexOf(";", sx);
                if (ex >= 0) {
                    pname = hctype.substring(sx, ex);
                } else {
                    pname = hctype.substring(sx);
                }
                pname = pname.trim();
                int xx = pname.length() - 1;
                if (pname.charAt(0) == '"' && pname.charAt(xx) == '"') {
                    pname = pname.substring(1, xx);
                }
            }
            if (pname == null) {
                return null;
            }
            logger.warn("Code in getPartName is still used. Please review it!");
            try {
                pname = MimeUtility.decodeText(pname);
            } catch (UnsupportedEncodingException ex) {
                Service.logger.error("Exception", ex);
            }
        }
        return pname;
    }

    public void processRequestCloudFile(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        try {
            String subject = ServletUtils.getStringParameter(request, "subject", true);
            String path = "/Uploads";
            int sid = vfsmanager.getMyDocumentsStoreId();
            FileObject fo = vfsmanager.getStoreFile(sid, path);
            if (!fo.exists())
                fo.createFolder();

            String dirname = PathUtils
                    .sanitizeFolderName(DateTimeUtils.createYmdHmsFormatter(environment.getProfile().getTimeZone())
                            .print(DateTimeUtils.now()) + " - " + subject);

            FileObject dir = fo.resolveFile(dirname);
            if (!dir.exists())
                dir.createFolder();

            JsonResult jsres = new JsonResult();
            jsres.put("storeId", sid);
            jsres.put("filePath", path + "/" + dirname + "/");
            jsres.printTo(out);
        } catch (Exception ex) {
            logger.error("Request cloud file failure", ex);
            new JsonResult(ex).printTo(out);
        }
    }

    public void processSaveColumnSize(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        String name = request.getParameter("name");
        String size = request.getParameter("size");
        us.setColumnSize(name, Integer.parseInt(size));
        out.println("{ result: true }");
    }

    public void processSaveColumnsOrder(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        try {
            String orderInfo = ServletUtils.getStringParameter(request, "orderInfo", true);
            ColumnsOrderSetting cos = ColumnsOrderSetting.fromJson(orderInfo);
            if (cos == null) {
                us.clearColumnsOrderSetting();
            } else {
                us.setColumnsOrderSetting(cos);
            }
            new JsonResult().printTo(out);

        } catch (Exception ex) {
            logger.error("Error saving columns order.", ex);
            new JsonResult(false).printTo(out);
        }
    }

    public void processSaveColumnVisibility(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        try {
            MailAccount account = getAccount(request);
            String folder = ServletUtils.getStringParameter(request, "folder", true);
            String name = ServletUtils.getStringParameter(request, "name", true);
            Boolean visible = ServletUtils.getBooleanParameter(request, "visible", false);

            ColumnVisibilitySetting cvs = us.getColumnVisibilitySetting(folder);
            FolderCache fc = account.getFolderCache(folder);

            cvs.put(name, visible);
            // Handle default cases...avoid data waste!
            //if(ColumnVisibilitySetting.isDefault(fc.isSent(), name, cvs.get(name))) cvs.remove(name);
            if (ColumnVisibilitySetting.isDefault(account.isUnderSentFolder(fc.getFolderName()), name,
                    cvs.get(name)))
                cvs.remove(name);

            if (cvs.isEmpty()) {
                us.clearColumnVisibilitySetting(folder);
            } else {
                us.setColumnVisibilitySetting(folder, cvs);
            }
            new JsonResult().printTo(out);

        } catch (Exception ex) {
            logger.error("Error saving column visibility.", ex);
            new JsonResult(false).printTo(out);
        }
    }

    public void processNewFolder(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String folder = request.getParameter("folder");
        String name = request.getParameter("name");
        String sout = null;
        FolderCache mcache = null;
        try {
            account.checkStoreConnected();
            Folder newfolder = null;
            boolean result = true;
            sout = "{\n";
            name = account.normalizeName(name);
            if (folder == null || (account.hasDifferentDefaultFolder() && folder.trim().length() == 0))
                mcache = account.getRootFolderCache();
            else
                mcache = account.getFolderCache(folder);

            newfolder = mcache.createFolder(name);
            if (newfolder == null) {
                result = false;
            } else {
                if (!account.isRoot(mcache)) {
                    sout += "parent: '" + StringEscapeUtils.escapeEcmaScript(mcache.getFolderName()) + "',\n";
                } else {
                    sout += "parent: null,\n";
                }
                sout += "name: '" + StringEscapeUtils.escapeEcmaScript(newfolder.getName()) + "',\n";
                sout += "fullname: '" + StringEscapeUtils.escapeEcmaScript(newfolder.getFullName()) + "',\n";
            }
            sout += "result: " + result + "\n}";
        } catch (MessagingException exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, oldid: '" + StringEscapeUtils.escapeEcmaScript(folder) + "', oldname: '"
                    + StringEscapeUtils.escapeEcmaScript(mcache != null ? mcache.getFolder().getName() : "unknown")
                    + "', text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        }
        out.println(sout);
    }

    public void processRenameFolder(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String folder = request.getParameter("folder");
        String name = request.getParameter("name");
        String sout = null;
        FolderCache mcache = null;
        MailUserSettings.FavoriteFolders favorites = us.getFavoriteFolders();

        try {
            account.checkStoreConnected();
            boolean result = true;
            sout = "{\n";
            mcache = account.getFolderCache(folder);
            name = account.normalizeName(name);
            String newid = account.renameFolder(folder, name);

            if (favorites.contains(account.getId(), folder)) {
                favorites.remove(account.getId(), folder);
                favorites.add(account.getId(), newid, name);
                us.setFavoriteFolders(favorites);
            }
            sout += "oldid: '" + StringEscapeUtils.escapeEcmaScript(folder) + "',\n";
            sout += "newid: '" + StringEscapeUtils.escapeEcmaScript(newid) + "',\n";
            sout += "newname: '" + StringEscapeUtils.escapeEcmaScript(name) + "',\n";
            sout += "result: " + result + "\n}";
        } catch (MessagingException exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, oldid: '" + StringEscapeUtils.escapeEcmaScript(folder) + "', oldname: '"
                    + StringEscapeUtils.escapeEcmaScript(mcache != null ? mcache.getFolder().getName() : "unknown")
                    + "', text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        }
        out.println(sout);
    }

    public void processHideFolder(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String folder = request.getParameter("folder");
        String sout = null;
        try {
            account.checkStoreConnected();
            boolean result = true;
            sout = "{\n";
            if (account.isSpecialFolder(folder)) {
                result = false;
            } else {
                hideFolder(account, folder);
            }
            sout += "result: " + result + "\n}";
        } catch (MessagingException exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        }
        out.println(sout);
    }

    public void processDeleteFolder(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String folder = request.getParameter("folder");
        String sout = null;
        FolderCache mcache = null;
        try {
            account.checkStoreConnected();
            boolean result = true;
            sout = "{\n";
            mcache = account.getFolderCache(folder);
            if (!account.isUnderFolder(account.getFolderArchive(), folder) && account.isSpecialFolder(folder)) {
                result = false;
            } else {
                result = account.deleteFolder(folder);
                if (result) {
                    sout += "oldid: '" + StringEscapeUtils.escapeEcmaScript(folder) + "',\n";
                }
            }
            sout += "result: " + result + "\n}";
        } catch (MessagingException exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, oldid: '" + StringEscapeUtils.escapeEcmaScript(folder) + "', oldname: '"
                    + StringEscapeUtils.escapeEcmaScript(mcache != null ? mcache.getFolder().getName() : "unknown")
                    + "', text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        }
        out.println(sout);
    }

    public void processTrashFolder(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String folder = request.getParameter("folder");
        String sout = null;
        FolderCache mcache = null;
        try {
            account.checkStoreConnected();
            boolean result = true;
            sout = "{\n";
            mcache = account.getFolderCache(folder);
            if (!account.isUnderFolder(account.getFolderArchive(), folder) && account.isSpecialFolder(folder)) {
                result = false;
            } else {
                FolderCache newfc = account.trashFolder(folder);
                if (newfc != null) {
                    sout += "newid: '" + StringEscapeUtils.escapeEcmaScript(newfc.getFolder().getFullName())
                            + "',\n";
                    sout += "trashid: '"
                            + StringEscapeUtils.escapeEcmaScript(newfc.getParent().getFolder().getFullName())
                            + "',\n";
                }
                result = true;
            }
            sout += "result: " + result + "\n}";
        } catch (MessagingException exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        }
        out.println(sout);
    }

    public void processMoveFolder(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String folder = request.getParameter("folder");
        String to = request.getParameter("to");
        String sout = null;
        FolderCache mcache = null;
        try {
            account.checkStoreConnected();
            boolean result = true;
            sout = "{\n";
            mcache = account.getFolderCache(folder);
            if (account.isSpecialFolder(folder)) {
                result = false;
            } else {
                FolderCache newfc = account.moveFolder(folder, to);
                Folder newf = newfc.getFolder();
                sout += "oldid: '" + StringEscapeUtils.escapeEcmaScript(folder) + "',\n";
                sout += "newid: '" + StringEscapeUtils.escapeEcmaScript(newf.getFullName()) + "',\n";
                sout += "newname: '" + StringEscapeUtils.escapeEcmaScript(newf.getName()) + "',\n";
                if (to != null) {
                    sout += "parent: '" + StringEscapeUtils.escapeEcmaScript(newf.getParent().getFullName())
                            + "',\n";
                }
                result = true;
            }
            sout += "result: " + result + "\n}";
        } catch (MessagingException exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, oldid: '" + StringEscapeUtils.escapeEcmaScript(folder) + "', oldname: '"
                    + StringEscapeUtils.escapeEcmaScript(mcache != null ? mcache.getFolder().getName() : "unknown")
                    + "', text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        }
        out.println(sout);
    }

    public void processEmptyFolder(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String folder = request.getParameter("folder");
        String sout = null;
        FolderCache mcache = null;
        try {
            account.checkStoreConnected();
            sout = "{\n";
            mcache = account.getFolderCache(folder);
            account.emptyFolder(folder);
            sout += "oldid: '" + StringEscapeUtils.escapeEcmaScript(folder) + "',\n";
            sout += "result: true\n}";
        } catch (MessagingException exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, oldid: '" + StringEscapeUtils.escapeEcmaScript(folder) + "', oldname: '"
                    + StringEscapeUtils.escapeEcmaScript(mcache != null ? mcache.getFolder().getName() : "unknown")
                    + "', text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        }
        out.println(sout);
    }

    public void processGetSource(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String foldername = request.getParameter("folder");
        String uid = request.getParameter("id");
        String sheaders = request.getParameter("headers");
        boolean headers = sheaders.equals("true");
        String sout = null;
        try {
            account.checkStoreConnected();
            //StringBuffer sb = new StringBuffer("<pre>");
            StringBuffer sb = new StringBuffer();
            FolderCache mcache = account.getFolderCache(foldername);
            Message msg = mcache.getMessage(Long.parseLong(uid));
            //Folder folder=msg.getFolder();
            for (Enumeration e = msg.getAllHeaders(); e.hasMoreElements();) {
                Header header = (Header) e.nextElement();
                //sb.append(MailUtils.htmlescape(header.getName()) + ": " + MailUtils.htmlescape(header.getValue()) + "\n");
                sb.append(header.getName() + ": " + header.getValue() + "\n");
            }
            if (!headers) {
                BufferedReader br = new BufferedReader(new InputStreamReader(msg.getInputStream()));
                String line = null;
                while ((line = br.readLine()) != null) {
                    //sb.append(MailUtils.htmlescape(line) + "\n");
                    sb.append(line + "\n");
                }
            }
            //sb.append("</pre>");
            sout = "{\nresult: true, source: '" + StringEscapeUtils.escapeEcmaScript(sb.toString()) + "'\n}";
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        }
        out.println(sout);
    }

    public void processSaveMail(HttpServletRequest request, HttpServletResponse response) {
        MailAccount account = getAccount(request);
        String foldername = request.getParameter("folder");
        String uid = request.getParameter("id");

        try {
            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(foldername);
            Message msg = mcache.getMessage(Long.parseLong(uid));
            String subject = msg.getSubject();
            ServletUtils.setFileStreamHeadersForceDownload(response, subject + ".eml");
            OutputStream out = response.getOutputStream();
            msg.writeTo(out);
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
        }
    }

    public void processDownloadMails(HttpServletRequest request, HttpServletResponse response) {
        MailAccount account = getAccount(request);
        String foldername = request.getParameter("folder");
        String sout = null;
        try {
            account.checkStoreConnected();
            StringBuffer sb = new StringBuffer();
            FolderCache mcache = account.getFolderCache(foldername);
            Message msgs[] = mcache.getAllMessages();
            String zipname = "Webtop-mails-" + getInternationalFolderName(mcache);
            response.setContentType("application/x-zip-compressed");
            response.setHeader("Content-Disposition", "inline; filename=\"" + zipname + ".zip\"");
            OutputStream out = response.getOutputStream();

            JarOutputStream jos = new java.util.jar.JarOutputStream(out);
            outputJarMailFolder(null, msgs, jos);

            int cut = foldername.length() + 1;
            cycleMailFolder(account, mcache.getFolder(), cut, jos);

            jos.close();

        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
        }
    }

    private void cycleMailFolder(MailAccount account, Folder folder, int cut, JarOutputStream jos)
            throws Exception {
        for (Folder child : folder.list()) {
            String fullname = child.getFullName();
            String relname = fullname.substring(cut).replace(folder.getSeparator(), '/');

            jos.putNextEntry(new JarEntry(relname + "/"));
            jos.closeEntry();

            cycleMailFolder(account, child, cut, jos);

            FolderCache mcache = account.getFolderCache(fullname);
            Message msgs[] = mcache.getAllMessages();
            if (msgs.length > 0)
                outputJarMailFolder(relname, msgs, jos);
        }
    }

    private void outputJarMailFolder(String foldername, Message msgs[], JarOutputStream jos) throws Exception {
        int digits = (msgs.length > 0 ? (int) Math.log10(msgs.length) + 1 : 1);
        for (int i = 0; i < msgs.length; ++i) {
            Message msg = msgs[i];
            String subject = msg.getSubject();
            if (subject != null)
                subject = subject.replace('/', '_').replace('\\', '_').replace(':', '-');
            else
                subject = "";
            java.util.Date date = msg.getReceivedDate();
            if (date == null)
                date = new java.util.Date();

            String fname = LangUtils.zerofill(i + 1, digits) + " - " + subject + ".eml";
            String fullname = null;
            if (foldername != null && !foldername.isEmpty())
                fullname = foldername + "/" + fname;
            else
                fullname = fname;
            JarEntry je = new JarEntry(fullname);
            je.setTime(date.getTime());
            jos.putNextEntry(je);
            msg.writeTo(jos);
            jos.closeEntry();
        }
        jos.flush();
    }

    public void processGetReplyMessage(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        UserProfile profile = environment.getProfile();
        //WebTopApp webtopapp=environment.getWebTopApp();
        String pfoldername = request.getParameter("folder");
        String puidmessage = request.getParameter("idmessage");
        String preplyall = request.getParameter("replyall");
        boolean replyAll = false;
        if (preplyall != null && preplyall.equals("1")) {
            replyAll = true;
        }
        String sout = null;
        try {
            String format = us.getFormat();
            boolean isHtml = format.equals("html");
            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(pfoldername);
            Message m = mcache.getMessage(Long.parseLong(puidmessage));
            if (m.isExpunged()) {
                throw new MessagingException("Message " + puidmessage + " expunged");
            }
            int newmsgid = getNewMessageID();
            SimpleMessage smsg = getReplyMsg(getNewMessageID(), account, m, replyAll,
                    account.isSentFolder(pfoldername), isHtml, profile.getEmailAddress(),
                    mprofile.isIncludeMessageInReply(), lookupResource(MailLocaleKey.MSG_FROMTITLE),
                    lookupResource(MailLocaleKey.MSG_TOTITLE), lookupResource(MailLocaleKey.MSG_CCTITLE),
                    lookupResource(MailLocaleKey.MSG_DATETITLE), lookupResource(MailLocaleKey.MSG_SUBJECTTITLE));
            sout = "{\n result: true,";
            Identity ident = mprofile.getIdentity(pfoldername);
            String inreplyto = smsg.getInReplyTo();
            String references[] = smsg.getReferences();
            sout += " replyfolder: '" + StringEscapeUtils.escapeEcmaScript(pfoldername) + "',";
            if (inreplyto != null) {
                sout += " inreplyto: '" + StringEscapeUtils.escapeEcmaScript(inreplyto) + "',";
            }
            if (references != null) {
                String refs = "";
                for (String s : references) {
                    refs += StringEscapeUtils.escapeEcmaScript(s) + " ";
                }
                sout += " references: '" + refs.trim() + "',";
            }
            String subject = smsg.getSubject();
            sout += " subject: '" + StringEscapeUtils.escapeEcmaScript(subject) + "',\n";
            sout += " recipients: [\n";
            String tos[] = smsg.getTo().split(";");
            boolean first = true;
            for (String to : tos) {
                if (!first) {
                    sout += ",\n";
                }
                sout += "   {rtype:'to',email:'" + StringEscapeUtils.escapeEcmaScript(to) + "'}";
                first = false;
            }
            String ccs[] = smsg.getCc().split(";");
            for (String cc : ccs) {
                if (!first) {
                    sout += ",\n";
                }
                sout += "   {rtype:'cc',email:'" + StringEscapeUtils.escapeEcmaScript(cc) + "'}";
                first = false;
            }
            sout += "\n ],\n";
            sout += " identityId: " + ident.getIdentityId() + ",\n";
            sout += " origuid:" + puidmessage + ",\n";
            if (isHtml) {
                String html = smsg.getContent();

                //cid inline attachments and relative html href substitution
                HTMLMailData maildata = mcache.getMailData((MimeMessage) m);
                first = true;
                sout += " attachments: [\n";

                for (int i = 0; i < maildata.getAttachmentPartCount(); ++i) {
                    try {
                        Part part = maildata.getAttachmentPart(i);
                        String filename = getPartName(part);
                        String cids[] = part.getHeader("Content-ID");
                        if (cids != null && cids[0] != null) {
                            String cid = cids[0];
                            if (cid.startsWith("<"))
                                cid = cid.substring(1);
                            if (cid.endsWith(">"))
                                cid = cid.substring(0, cid.length() - 1);
                            String mime = MailUtils.getMediaTypeFromHeader(part.getContentType());
                            UploadedFile upfile = addAsUploadedFile("" + newmsgid, filename, mime,
                                    part.getInputStream());
                            if (!first) {
                                sout += ",\n";
                            }
                            sout += "{ " + " uploadId: '" + StringEscapeUtils.escapeEcmaScript(upfile.getUploadId())
                                    + "', " + " fileName: '" + StringEscapeUtils.escapeEcmaScript(filename) + "', "
                                    + " cid: '" + StringEscapeUtils.escapeEcmaScript(cid) + "', "
                                    + " inline: true, " + " fileSize: " + upfile.getSize() + ", " + " editable: "
                                    + isFileEditableInDocEditor(filename) + " " + " }";
                            first = false;
                            //TODO: change this weird matching of cids2urls!
                            html = StringUtils.replace(html, "cid:" + cid,
                                    "service-request?csrf=" + getEnv().getCSRFToken() + "&service=" + SERVICE_ID
                                            + "&action=PreviewAttachment&nowriter=true&uploadId="
                                            + upfile.getUploadId() + "&cid=" + cid);
                        }
                    } catch (Exception exc) {
                        Service.logger.error("Exception", exc);
                    }
                }

                sout += "\n ],\n";

                sout += " content:'" + StringEscapeUtils.escapeEcmaScript(html) + "',\n";
            } else {
                String text = smsg.getTextContent();
                sout += " content:'" + StringEscapeUtils.escapeEcmaScript(text) + "',\n";
            }
            sout += " format:'" + format + "'\n";
            sout += "\n}";
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        }
        if (sout != null) {
            out.println(sout);
        }
    }

    public void processGetForwardMessage(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        MailAccount account = getAccount(request);
        UserProfile profile = environment.getProfile();
        String pfoldername = request.getParameter("folder");
        String puidmessage = request.getParameter("idmessage");
        String pnewmsgid = request.getParameter("newmsgid");
        String pattached = request.getParameter("attached");
        boolean attached = (pattached != null && pattached.equals("1"));
        long newmsgid = Long.parseLong(pnewmsgid);
        String sout = null;
        try {
            String format = us.getFormat();
            boolean isHtml = format.equals("html");
            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(pfoldername);
            Message m = mcache.getMessage(Long.parseLong(puidmessage));
            if (m.isExpunged()) {
                throw new MessagingException("Message " + puidmessage + " expunged");
            }
            SimpleMessage smsg = getForwardMsg(newmsgid, m, isHtml, lookupResource(MailLocaleKey.MSG_FROMTITLE),
                    lookupResource(MailLocaleKey.MSG_TOTITLE), lookupResource(MailLocaleKey.MSG_CCTITLE),
                    lookupResource(MailLocaleKey.MSG_DATETITLE), lookupResource(MailLocaleKey.MSG_SUBJECTTITLE),
                    attached);

            sout = "{\n result: true,";
            Identity ident = mprofile.getIdentity(pfoldername);
            String forwardedfrom = smsg.getForwardedFrom();
            sout += " forwardedfolder: '" + StringEscapeUtils.escapeEcmaScript(pfoldername) + "',";
            if (forwardedfrom != null) {
                sout += " forwardedfrom: '" + StringEscapeUtils.escapeEcmaScript(forwardedfrom) + "',";
            }
            String subject = smsg.getSubject();
            sout += " subject: '" + StringEscapeUtils.escapeEcmaScript(subject) + "',\n";

            String html = smsg.getContent();
            String text = smsg.getTextContent();
            if (!attached) {
                HTMLMailData maildata = mcache.getMailData((MimeMessage) m);
                boolean first = true;
                sout += " attachments: [\n";

                for (int i = 0; i < maildata.getAttachmentPartCount(); ++i) {
                    try {
                        Part part = maildata.getAttachmentPart(i);
                        String filename = getPartName(part);
                        if (!part.isMimeType("message/*")) {
                            String cids[] = part.getHeader("Content-ID");
                            String cid = null;
                            //String cid=filename;
                            if (cids != null && cids[0] != null) {
                                cid = cids[0];
                                if (cid.startsWith("<"))
                                    cid = cid.substring(1);
                                if (cid.endsWith(">"))
                                    cid = cid.substring(0, cid.length() - 1);
                            }

                            if (filename == null)
                                filename = cid;
                            String mime = MailUtils.getMediaTypeFromHeader(part.getContentType());
                            UploadedFile upfile = addAsUploadedFile(pnewmsgid, filename, mime,
                                    part.getInputStream());
                            boolean inline = false;
                            if (part.getDisposition() != null) {
                                inline = part.getDisposition().equalsIgnoreCase(Part.INLINE);
                            }
                            if (!first) {
                                sout += ",\n";
                            }
                            sout += "{ " + " uploadId: '" + StringEscapeUtils.escapeEcmaScript(upfile.getUploadId())
                                    + "', " + " fileName: '" + StringEscapeUtils.escapeEcmaScript(filename) + "', "
                                    + " cid: "
                                    + (cid == null ? null : "'" + StringEscapeUtils.escapeEcmaScript(cid) + "'")
                                    + ", " + " inline: " + inline + ", " + " fileSize: " + upfile.getSize() + ", "
                                    + " editable: " + isFileEditableInDocEditor(filename) + " " + " }";
                            first = false;
                            //TODO: change this weird matching of cids2urls!
                            html = StringUtils.replace(html, "cid:" + cid,
                                    "service-request?csrf=" + getEnv().getCSRFToken() + "&service=" + SERVICE_ID
                                            + "&action=PreviewAttachment&nowriter=true&uploadId="
                                            + upfile.getUploadId() + "&cid=" + cid);
                        }
                    } catch (Exception exc) {
                        Service.logger.error("Exception", exc);
                    }
                }

                sout += "\n ],\n";
                //String surl = "service-request?service="+SERVICE_ID+"&action=PreviewAttachment&nowriter=true&newmsgid=" + newmsgid + "&cid=";
                //html = replaceCidUrls(html, maildata, surl);
            } else {
                String filename = m.getSubject() + ".eml";
                UploadedFile upfile = addAsUploadedFile(pnewmsgid, filename, "message/rfc822",
                        ((IMAPMessage) m).getMimeStream());
                sout += " attachments: [\n";
                sout += "{ " + " uploadId: '" + StringEscapeUtils.escapeEcmaScript(upfile.getUploadId()) + "', "
                        + " fileName: '" + StringEscapeUtils.escapeEcmaScript(filename) + "', " + " cid: null, "
                        + " inline: false, " + " fileSize: " + upfile.getSize() + ", " + " editable: "
                        + isFileEditableInDocEditor(filename) + " " + " }";
                sout += "\n ],\n";
            }
            sout += " identityId: " + ident.getIdentityId() + ",\n";
            sout += " origuid:" + puidmessage + ",\n";
            if (isHtml) {
                sout += " content:'" + StringEscapeUtils.escapeEcmaScript(html) + "',\n";
            } else {
                sout += " content:'" + StringEscapeUtils.escapeEcmaScript(text) + "',\n";
            }
            sout += " format:'" + format + "'\n";
            sout += "\n}";
            out.println(sout);
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
            out.println(sout);
        }
    }

    private boolean isPreviewBalanceTags(InternetAddress ia) {
        return previewBalanceTags;
    }

    public void processGetEditMessage(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String pfoldername = request.getParameter("folder");
        String puidmessage = request.getParameter("idmessage");
        String pnewmsgid = request.getParameter("newmsgid");
        long newmsgid = Long.parseLong(pnewmsgid);
        String sout = null;
        try {
            MailEditFormat editFormat = ServletUtils.getEnumParameter(request, "format", null,
                    MailEditFormat.class);
            if (editFormat == null)
                editFormat = EnumUtils.forSerializedName(us.getFormat(), MailEditFormat.HTML, MailEditFormat.class);
            boolean isPlainEdit = MailEditFormat.PLAIN_TEXT.equals(editFormat);

            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(pfoldername);
            IMAPMessage m = (IMAPMessage) mcache.getMessage(Long.parseLong(puidmessage));
            m.setPeek(us.isManualSeen());
            //boolean wasseen = m.isSet(Flags.Flag.SEEN);
            String vheader[] = m.getHeader("Disposition-Notification-To");
            boolean receipt = false;
            int priority = 3;
            boolean recipients = false;
            boolean toDelete = false;
            if (account.isDraftsFolder(pfoldername)) {
                if (vheader != null && vheader[0] != null) {
                    receipt = true;
                }
                priority = getPriority(m);
                recipients = true;

                //if autosaved drafts, delete
                String values[] = m.getHeader(HEADER_X_WEBTOP_MSGID);
                if (values != null && values.length > 0) {
                    try {
                        long msgId = Long.parseLong(values[0]);
                        if (msgId > 0) {
                            toDelete = true;
                        }
                    } catch (NumberFormatException exc) {

                    }
                }
            }

            sout = "{\n result: true,";
            String subject = m.getSubject();
            if (subject == null) {
                subject = "";
            }
            sout += " subject: '" + StringEscapeUtils.escapeEcmaScript(subject) + "',\n";

            String inreplyto = null;
            String references[] = null;
            String vs[] = m.getHeader("In-Reply-To");
            if (vs != null && vs[0] != null) {
                inreplyto = vs[0];
            }
            references = m.getHeader("References");
            if (inreplyto != null) {
                vs = m.getHeader("Sonicle-reply-folder");
                String replyfolder = null;
                if (vs != null && vs[0] != null) {
                    replyfolder = vs[0];
                }
                if (replyfolder != null) {
                    sout += " replyfolder: '" + StringEscapeUtils.escapeEcmaScript(replyfolder) + "',";
                }
                sout += " inreplyto: '" + StringEscapeUtils.escapeEcmaScript(inreplyto) + "',";
            }
            if (references != null) {
                String refs = "";
                for (String s : references) {
                    refs += StringEscapeUtils.escapeEcmaScript(s) + " ";
                }
                sout += " references: '" + refs.trim() + "',";
            }

            String forwardedfrom = null;
            vs = m.getHeader("Forwarded-From");
            if (vs != null && vs[0] != null) {
                forwardedfrom = vs[0];
            }
            if (forwardedfrom != null) {
                vs = m.getHeader("Sonicle-forwarded-folder");
                String forwardedfolder = null;
                if (vs != null && vs[0] != null) {
                    forwardedfolder = vs[0];
                }
                if (forwardedfolder != null) {
                    sout += " forwardedfolder: '" + StringEscapeUtils.escapeEcmaScript(forwardedfolder) + "',";
                }
                sout += " forwardedfrom: '" + StringEscapeUtils.escapeEcmaScript(forwardedfrom) + "',";
            }

            sout += " receipt: " + receipt + ",\n";
            sout += " priority: " + (priority >= 3 ? false : true) + ",\n";

            Identity ident = null;
            Address from[] = m.getFrom();
            InternetAddress iafrom = null;
            if (from != null && from.length > 0) {
                iafrom = (InternetAddress) from[0];
                String email = iafrom.getAddress();
                String displayname = iafrom.getPersonal();
                if (displayname == null)
                    displayname = email;
                //sout+=" from: { email: '"+StringEscapeUtils.escapeEcmaScript(email)+"', displayname: '"+StringEscapeUtils.escapeEcmaScript(displayname)+"' },\n";
                ident = mprofile.getIdentity(displayname, email);
            }

            sout += " recipients: [\n";
            if (recipients) {
                Address tos[] = m.getRecipients(RecipientType.TO);
                String srec = "";
                if (tos != null) {
                    for (Address to : tos) {
                        if (srec.length() > 0) {
                            srec += ",\n";
                        }
                        srec += "   { " + "rtype: 'to', " + "email: '"
                                + StringEscapeUtils.escapeEcmaScript(getDecodedAddress(to)) + "'" + " }";
                    }
                }
                Address ccs[] = m.getRecipients(RecipientType.CC);
                if (ccs != null) {
                    for (Address cc : ccs) {
                        if (srec.length() > 0) {
                            srec += ",\n";
                        }
                        srec += "   { " + "rtype: 'cc', " + "email: '"
                                + StringEscapeUtils.escapeEcmaScript(getDecodedAddress(cc)) + "'" + " }";
                    }
                }
                Address bccs[] = m.getRecipients(RecipientType.BCC);
                if (bccs != null) {
                    for (Address bcc : bccs) {
                        if (srec.length() > 0) {
                            srec += ",\n";
                        }
                        srec += "   { " + "rtype: 'bcc', " + "email: '"
                                + StringEscapeUtils.escapeEcmaScript(getDecodedAddress(bcc)) + "'" + " }";
                    }
                }

                sout += srec;
            } else {
                sout += "   { " + "rtype: 'to', " + "email: ''" + " }";

            }
            sout += " ],\n";

            String html = "";
            boolean balanceTags = isPreviewBalanceTags(iafrom);
            ArrayList<String> htmlparts = mcache.getHTMLParts((MimeMessage) m, newmsgid, true, balanceTags);
            for (String xhtml : htmlparts) {
                html += xhtml + "<BR><BR>";
            }
            HTMLMailData maildata = mcache.getMailData((MimeMessage) m);
            //if(!wasseen){
            //   if (us.isManualSeen()) {
            //      m.setFlag(Flags.Flag.SEEN, false);
            //   }
            //}

            boolean first = true;
            sout += " attachments: [\n";
            for (int i = 0; i < maildata.getAttachmentPartCount(); ++i) {
                Part part = maildata.getAttachmentPart(i);
                String filename = getPartName(part);

                String cids[] = part.getHeader("Content-ID");
                String cid = null;
                //String cid=filename;
                if (cids != null && cids[0] != null) {
                    cid = cids[0];
                    if (cid.startsWith("<"))
                        cid = cid.substring(1);
                    if (cid.endsWith(">"))
                        cid = cid.substring(0, cid.length() - 1);
                }

                if (filename == null) {
                    filename = cid;
                }
                String mime = part.getContentType();
                UploadedFile upfile = addAsUploadedFile(pnewmsgid, filename, mime, part.getInputStream());
                boolean inline = false;
                if (part.getDisposition() != null) {
                    inline = part.getDisposition().equalsIgnoreCase(Part.INLINE);
                }
                if (!first) {
                    sout += ",\n";
                }
                sout += "{ " + " uploadId: '" + StringEscapeUtils.escapeEcmaScript(upfile.getUploadId()) + "', "
                        + " fileName: '" + StringEscapeUtils.escapeEcmaScript(filename) + "', " + " cid: "
                        + (cid == null ? null : "'" + StringEscapeUtils.escapeEcmaScript(cid) + "'") + ", "
                        + " inline: " + inline + ", " + " fileSize: " + upfile.getSize() + " " + " }";
                first = false;
                //TODO: change this weird matching of cids2urls!
                html = StringUtils.replace(html, "cid:" + cid,
                        "service-request?csrf=" + getEnv().getCSRFToken() + "&service=" + SERVICE_ID
                                + "&action=PreviewAttachment&nowriter=true&uploadId=" + upfile.getUploadId()
                                + "&cid=" + cid);

            }
            sout += "\n ],\n";

            if (ident != null)
                sout += " identityId: " + ident.getIdentityId() + ",\n";
            sout += " origuid:" + puidmessage + ",\n";
            sout += " deleted:" + toDelete + ",\n";
            sout += " folder:'" + pfoldername + "',\n";
            sout += " content:'" + (isPlainEdit
                    ? StringEscapeUtils.escapeEcmaScript(MailUtils.htmlToText(MailUtils.htmlunescapesource(html)))
                    : StringEscapeUtils.escapeEcmaScript(html)) + "',\n";
            sout += " format:'" + EnumUtils.toSerializedName(editFormat) + "'\n";
            sout += "\n}";

            m.setPeek(false);

            if (toDelete) {
                m.setFlag(Flags.Flag.DELETED, true);
                m.getFolder().expunge();
            }

            out.println(sout);
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            sout = "{\nresult: false, text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}";
        }
    }

    public void processManageMessage(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        try {
            String sendAction = ServletUtils.getStringParameter(request, "sendAction", "save");

            if (sendAction.equals("save")) {
                processSaveMessage(request, response, out);
            } else if (sendAction.equals("send")) {
                processSendMessage(request, response, out);
            } else if (sendAction.equals("schedule")) {
                processScheduleMessage(request, response, out);
            } else {
                throw new Exception("Invlid send action operation " + sendAction);
            }
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            Throwable cause = exc.getCause();
            String msg = cause != null ? cause.getMessage() : exc.getMessage();
            JsonResult json = new JsonResult(false, msg);
            json.printTo(out);
        }
    }

    public void processSendMessage(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        JsonResult json = null;
        CoreManager coreMgr = WT.getCoreManager();
        IContactsManager contactsManager = (IContactsManager) WT.getServiceManager("com.sonicle.webtop.contacts",
                true, environment.getProfileId());

        // TODO: Cloud integration!!!
        /*        VFSService vfs=(VFSService)wts.getServiceByName("vfs");
               ArrayList<String> hashlinks=null;
               if (vfs!=null) {
               //look for links to cloud in the html
               String html=request.getParameter("content");
               hashlinks=new ArrayList<String>();
               int hlx=-1;
               String puburl=wtd.getSetting("vfs.pub.url");
               char chars[]=new char[] { '\'', '"'};
               for(char c: chars) {
               String pattern="<a href="+c+puburl+"/public/vfs/";
               Service.logger.debug("Looking for pattern "+pattern);
               while((hlx=html.indexOf(pattern,hlx+1))>=0) {
               int xhash1=hlx+pattern.length();
               int xhash2=html.indexOf(c,xhash1);
               if (xhash2>xhash1) {
               String hash=html.substring(xhash1,xhash2);
               Service.logger.debug("Found hash "+hash);
               hashlinks.add(hash);
               }
               }
               }
               }*/
        try {
            MailAccount account = getAccount(request);
            //String emails[]=request.getParameterValues("recipients");
            Payload<MapItem, JsMessage> pl = ServletUtils.getPayload(request, JsMessage.class);
            JsMessage jsmsg = pl.data;
            long msgId = ServletUtils.getLongParameter(request, "msgId", true);
            boolean isFax = ServletUtils.getBooleanParameter(request, "isFax", false);
            boolean save = ServletUtils.getBooleanParameter(request, "save", false);

            if (isFax) {
                int faxmaxtos = getEnv().getCoreServiceSettings().getFaxMaxRecipients();

                //check for valid fax recipients
                String faxpattern = getEnv().getCoreServiceSettings().getFaxPattern();
                String regex = "^" + faxpattern.replace("{number}", "(\\d+)").replace("{username}", "(\\w+)") + "$";
                Pattern pattern = Pattern.compile(regex);
                int nemails = 0;
                for (JsRecipient jsr : jsmsg.recipients) {
                    if (StringUtils.isEmpty(jsr.email))
                        continue;
                    ++nemails;
                    if (StringUtils.isNumeric(jsr.email))
                        continue;
                    boolean matches = false;
                    try {
                        InternetAddress ia = new InternetAddress(jsr.email);
                        String email = ia.getAddress();
                        matches = pattern.matcher(email).matches();
                    } catch (Exception exc) {

                    }
                    if (!matches) {
                        throw new Exception(lookupResource(MailLocaleKey.FAX_ADDRESS_ERROR));
                    }
                }
                if (faxmaxtos > 0 && nemails > faxmaxtos) {
                    throw new WTException(lookupResource(MailLocaleKey.FAX_MAXADDRESS_ERROR), faxmaxtos);
                }

            }

            account.checkStoreConnected();
            //String attachments[] = request.getParameterValues("attachments");
            //if (attachments == null) {
            //   attachments = new String[0];
            //}
            SimpleMessage msg = prepareMessage(jsmsg, msgId, save, isFax);
            Identity ifrom = msg.getFrom();
            String from = environment.getProfile().getEmailAddress();
            if (ifrom != null) {
                from = ifrom.getEmail();
            }
            account.checkStoreConnected();
            Exception exc = sendMessage(msg, jsmsg.attachments);
            if (exc == null) {
                //if is draft, check for deletion
                if (jsmsg.draftuid > 0 && jsmsg.draftfolder != null && ss.isDefaultFolderDraftsDeleteMsgOnSend()) {
                    FolderCache fc = account.getFolderCache(jsmsg.draftfolder);
                    fc.deleteMessages(new long[] { jsmsg.draftuid }, false);
                }

                //Save used recipients
                for (JsRecipient rcpt : pl.data.recipients) {
                    String email = rcpt.email;
                    if (email != null && email.trim().length() > 0)
                        coreMgr.autoLearnInternetRecipient(email);
                }

                //Save subject for suggestions
                if (jsmsg.subject != null && jsmsg.subject.trim().length() > 0)
                    WT.getCoreManager().addServiceStoreEntry(SERVICE_ID, "subject", jsmsg.subject.toUpperCase(),
                            jsmsg.subject);

                coreMgr.deleteMyAutosaveData(getEnv().getClientTrackingID(), SERVICE_ID, "newmail", "" + msgId);

                deleteAutosavedDraft(account, msgId);
                // TODO: Cloud integration!!! Destination emails added to share
                /*                if (vfs!=null && hashlinks!=null && hashlinks.size()>0) {
                             for(String hash: hashlinks) {
                             Service.logger.debug("Adding emails to hash "+hash);
                             vfs.setAuthEmails(hash, from, emails);
                             }
                    
                             }*/
                FolderCache fc = account.getFolderCache(account.getFolderSent());
                fc.setForceRefresh();
                //check for in-reply-to and set the answered flags
                //String inreplyto = request.getParameter("inreplyto");
                //String replyfolder = request.getParameter("replyfolder");
                //String forwardedfrom = request.getParameter("forwardedfrom");
                //String forwardedfolder = request.getParameter("forwardedfolder");
                //String soriguid=request.getParameter("origuid");
                //long origuid=0;
                //try { origuid=Long.parseLong(soriguid); } catch(RuntimeException rexc) {}
                String foundfolder = null;
                if (jsmsg.forwardedfrom != null && jsmsg.forwardedfrom.trim().length() > 0) {
                    try {
                        foundfolder = foundfolder = flagForwardedMessage(account, jsmsg.forwardedfolder,
                                jsmsg.forwardedfrom, jsmsg.origuid);
                    } catch (Exception xexc) {
                        Service.logger.error("Exception", xexc);
                    }
                } else if ((jsmsg.inreplyto != null && jsmsg.inreplyto.trim().length() > 0)
                        || (jsmsg.replyfolder != null && jsmsg.replyfolder.trim().length() > 0
                                && jsmsg.origuid > 0)) {
                    try {

                        String[] toRecipients = SimpleMessage.breakAddr(msg.getTo());

                        for (String toRecipient : toRecipients) {
                            InternetAddress internetAddress = getInternetAddress(toRecipient);
                            String contactEmail = internetAddress.getAddress();
                            String contactPersonal = internetAddress.getPersonal();

                            Condition<ContactQuery> predicate = new ContactQuery().email().eq(contactEmail);

                            List<Integer> myCategories = contactsManager.listCategoryIds();
                            List<Integer> sharedCategories = contactsManager.listIncomingCategoryIds();
                            myCategories.addAll(sharedCategories);

                            boolean existsContact = contactsManager.existContact(myCategories, predicate);

                            if (!existsContact) {
                                sendAddContactMessage(contactEmail, contactPersonal);
                                break;
                            }
                        }

                        foundfolder = flagAnsweredMessage(account, jsmsg.replyfolder, jsmsg.inreplyto,
                                jsmsg.origuid);
                    } catch (Exception xexc) {
                        Service.logger.error("Exception", xexc);
                    }
                }

                json = new JsonResult().set("foundfolder", foundfolder).set("saved", Boolean.FALSE);
            } else {
                Throwable cause = exc.getCause();
                String msgstr = cause != null ? cause.getMessage() : exc.getMessage();
                json = new JsonResult(false, msgstr);
            }
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            Throwable cause = exc.getCause();
            String msg = cause != null ? cause.getMessage() : exc.getMessage();
            json = new JsonResult(false, msg);
        }
        json.printTo(out);
    }

    private void sendAddContactMessage(String email, String personal) {
        this.environment.notify(new AddContactMessage(email, personal));
    }

    private String flagAnsweredMessage(MailAccount account, String replyfolder, String id, long origuid)
            throws MessagingException {
        String foundfolder = null;
        if (replyfolder != null) {
            if (_flagAnsweredMessage(account, replyfolder, origuid))
                foundfolder = replyfolder;
        }
        if (foundfolder == null) {
            SonicleIMAPFolder.RecursiveSearchResult rsr = account.recursiveSearchByMessageID("", id);
            if (rsr != null) {
                _flagAnsweredMessage(account, rsr.foldername, rsr.uid);
                foundfolder = rsr.foldername;
            }
        }
        return foundfolder;
    }

    private boolean _flagAnsweredMessage(MailAccount account, String foldername, long uid)
            throws MessagingException {
        Message msg = null;
        SonicleIMAPFolder sifolder = (SonicleIMAPFolder) account.getFolder(foldername);
        sifolder.open(Folder.READ_WRITE);
        msg = sifolder.getMessageByUID(uid);
        boolean found = msg != null;
        if (found) {
            msg.setFlags(FolderCache.repliedFlags, true);
        }
        sifolder.close(true);
        return found;
    }

    private String flagForwardedMessage(MailAccount account, String forwardedfolder, String id, long origuid)
            throws MessagingException {
        String foundfolder = null;
        if (forwardedfolder != null) {
            if (_flagForwardedMessage(account, forwardedfolder, origuid))
                foundfolder = forwardedfolder;
        }
        if (foundfolder == null) {
            SonicleIMAPFolder.RecursiveSearchResult rsr = account.recursiveSearchByMessageID("", id);
            if (rsr != null) {
                _flagForwardedMessage(account, rsr.foldername, rsr.uid);
                foundfolder = rsr.foldername;
            }
        }
        return foundfolder;
    }

    private boolean _flagForwardedMessage(MailAccount account, String foldername, long uid)
            throws MessagingException {
        Message msg = null;
        SonicleIMAPFolder sifolder = (SonicleIMAPFolder) account.getFolder(foldername);
        sifolder.open(Folder.READ_WRITE);
        msg = sifolder.getMessageByUID(uid);
        boolean found = msg != null;
        if (found) {
            msg.setFlags(FolderCache.forwardedFlags, true);
        }
        sifolder.close(true);
        return found;
    }

    public void processSaveMessage(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        JsonResult json = null;
        CoreManager coreMgr = WT.getCoreManager();
        try {
            account.checkStoreConnected();
            Payload<MapItem, JsMessage> pl = ServletUtils.getPayload(request, JsMessage.class);
            JsMessage jsmsg = pl.data;
            long msgId = ServletUtils.getLongParameter(request, "msgId", true);
            //String attachments[] = request.getParameterValues("attachments");
            String savefolder = ServletUtils.getStringParameter(request, "savefolder", false);
            //if (attachments == null) {
            //   attachments = new String[0];
            //}
            SimpleMessage msg = prepareMessage(jsmsg, msgId, false, false);
            account.checkStoreConnected();
            FolderCache fc = null;
            if (savefolder == null) {
                fc = determineSentFolder(account, msg);
            } else {
                fc = account.getFolderCache(savefolder);
            }
            Exception exc = saveMessage(msg, jsmsg.attachments, fc);
            if (exc == null) {
                coreMgr.deleteMyAutosaveData(getEnv().getClientTrackingID(), SERVICE_ID, "newmail", "" + msgId);

                deleteAutosavedDraft(account, msgId);

                if (pl.data.origuid > 0 && pl.data.folder != null
                        && fc.getFolder().getFullName().equals(pl.data.folder)) {
                    fc.deleteMessages(new long[] { pl.data.origuid }, false);
                }

                fc.setForceRefresh();
                json = new JsonResult().set("saved", Boolean.TRUE);
            } else {
                json = new JsonResult(false, exc.getMessage());
            }
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            json = new JsonResult(false, exc.getMessage());
        }
        json.printTo(out);
    }

    private FolderCache determineSentFolder(MailAccount account, SimpleMessage msg) throws MessagingException {
        String draftsfolder = account.getFolderDrafts();
        Identity ident = msg.getFrom();
        if (ident != null) {
            String mainfolder = ident.getMainFolder();
            if (mainfolder != null && mainfolder.trim().length() > 0) {
                String newdraftsfolder = mainfolder + account.getFolderSeparator()
                        + account.getLastFolderName(draftsfolder);
                try {
                    Folder folder = account.getFolder(newdraftsfolder);
                    if (folder.exists()) {
                        draftsfolder = newdraftsfolder;
                    }
                } catch (MessagingException exc) {
                    logger.error("Error on identity {}/{} Drafts Folder", environment.getProfile().getUserId(),
                            ident.getEmail(), exc);
                }
            }
        }
        FolderCache fc = account.getFolderCache(draftsfolder);
        return fc;
    }

    public void processScheduleMessage(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        JsonResult json = null;
        CoreManager coreMgr = WT.getCoreManager();
        try {
            MailAccount account = getAccount(request);
            account.checkStoreConnected();
            Payload<MapItem, JsMessage> pl = ServletUtils.getPayload(request, JsMessage.class);
            JsMessage jsmsg = pl.data;
            long msgId = ServletUtils.getLongParameter(request, "msgId", true);
            String savefolder = ServletUtils.getStringParameter(request, "savefolder", false);
            String scheddate = ServletUtils.getStringParameter(request, "scheddate", true);
            String schedtime = ServletUtils.getStringParameter(request, "schedtime", true);
            String schednotify = ServletUtils.getStringParameter(request, "schednotify", true);

            /*if (attachments == null) {
               attachments = new String[0];
            }*/
            SimpleMessage msg = prepareMessage(jsmsg, msgId, false, false);
            account.checkStoreConnected();
            FolderCache fc = null;
            if (savefolder == null) {
                fc = account.getFolderCache(account.getFolderDrafts());
            } else {
                fc = account.getFolderCache(savefolder);
            }
            Exception exc = scheduleMessage(msg, jsmsg.attachments, fc, scheddate, schedtime, schednotify);
            if (exc == null) {
                coreMgr.deleteMyAutosaveData(getEnv().getClientTrackingID(), SERVICE_ID, "newmail", "" + msgId);

                deleteAutosavedDraft(account, msgId);

                fc.setForceRefresh();
                json = new JsonResult().set("saved", Boolean.TRUE);
            } else {
                json = new JsonResult(false, exc.getMessage());
            }
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            json = new JsonResult(false, exc.getMessage());
        }
        json.printTo(out);
    }

    public void processDiscardMessage(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        JsonResult json = null;
        CoreManager coreMgr = WT.getCoreManager();
        try {
            MailAccount account = getAccount(request);
            long msgId = ServletUtils.getLongParameter(request, "msgId", true);
            deleteCloudAttachments(msgId);
            coreMgr.deleteMyAutosaveData(getEnv().getClientTrackingID(), SERVICE_ID, "newmail", "" + msgId);
            deleteAutosavedDraft(account, msgId);
            json = new JsonResult();
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            json = new JsonResult(false, exc.getMessage());
        }
        json.printTo(out);
    }

    private SimpleMessage prepareMessage(JsMessage jsmsg, long msgId, boolean save, boolean isFax)
            throws Exception {
        PrivateEnvironment env = environment;
        UserProfile profile = env.getProfile();
        //expand multiple addresses
        ArrayList<String> aemails = new ArrayList<>();
        ArrayList<String> artypes = new ArrayList<>();
        for (JsRecipient jsrcpt : jsmsg.recipients) {
            String emails[] = StringUtils.split(jsrcpt.email, ';');
            for (String email : emails) {
                aemails.add(email);
                artypes.add(jsrcpt.rtype);
            }
        }
        String emails[] = new String[aemails.size()];
        emails = (String[]) aemails.toArray(emails);
        String rtypes[] = new String[artypes.size()];
        rtypes = (String[]) artypes.toArray(rtypes);

        //String replyfolder = request.getParameter("replyfolder");
        //String inreplyto = request.getParameter("inreplyto");
        //String references = request.getParameter("references");

        //String forwardedfolder = request.getParameter("forwardedfolder");
        //String forwardedfrom = request.getParameter("forwardedfrom");

        //String subject = request.getParameter("subject");
        //String mime = request.getParameter("mime");
        //String sident = request.getParameter("identity");
        //String content = request.getParameter("content");
        //String msgid = request.getParameter("newmsgid");
        //String ssave = request.getParameter("save");
        //boolean save = (ssave != null && ssave.equals("true"));
        //String sreceipt = request.getParameter("receipt");
        //boolean receipt = (sreceipt != null && sreceipt.equals("true"));
        //String spriority = request.getParameter("priority");
        //boolean priority = (spriority != null && spriority.equals("true"));

        //boolean isFax = request.getParameter("fax") != null;

        String to = null;
        String cc = null;
        String bcc = null;
        for (int i = 0; i < emails.length; ++i) {
            //String email=decoder.decode(ByteBuffer.wrap(emails[i].getBytes())).toString();
            String email = emails[i];
            if (email == null || email.trim().length() == 0) {
                continue;
            }
            //Check for list
            boolean checkemail = true;
            boolean listdone = false;
            if (email.indexOf('@') < 0) {
                if (isFax && StringUtils.isNumeric(email)) {
                    String faxpattern = getEnv().getCoreServiceSettings().getFaxPattern();
                    String faxemail = faxpattern.replace("{number}", email).replace("{username}",
                            profile.getUserId());
                    email = faxemail;
                }
            } else {
                //check for list if one email with domain equals one allowed service id
                InternetAddress ia = null;
                try {
                    ia = new InternetAddress(email);
                } catch (AddressException exc) {

                }
                if (ia != null) {
                    String iamail = ia.getAddress();
                    String dom = iamail.substring(iamail.indexOf("@") + 1);
                    CoreManager core = WT.getCoreManager();
                    if (environment.getSession().isServiceAllowed(dom)) {
                        List<Recipient> rcpts = core.expandVirtualProviderRecipient(iamail);
                        for (Recipient rcpt : rcpts) {
                            String xemail = rcpt.getAddress();
                            String xpersonal = rcpt.getPersonal();
                            String xrtype = EnumUtils.toSerializedName(rcpt.getType());

                            if (xpersonal != null)
                                xemail = xpersonal + " <" + xemail + ">";

                            try {
                                checkEmail(xemail);
                                InternetAddress.parse(xemail.replace(',', ' '), true);
                            } catch (AddressException exc) {
                                throw new AddressException(
                                        lookupResource(MailLocaleKey.ADDRESS_ERROR) + " : " + xemail);
                            }
                            if (rtypes[i].equals("to")) {
                                if (xrtype.equals("to")) {
                                    if (to == null)
                                        to = xemail;
                                    else
                                        to += "; " + xemail;
                                } else if (xrtype.equals("cc")) {
                                    if (cc == null)
                                        cc = xemail;
                                    else
                                        cc += "; " + xemail;
                                } else if (xrtype.equals("bcc")) {
                                    if (bcc == null)
                                        bcc = xemail;
                                    else
                                        bcc += "; " + xemail;
                                }
                            } else if (rtypes[i].equals("cc")) {
                                if (cc == null)
                                    cc = xemail;
                                else
                                    cc += "; " + xemail;
                            } else if (rtypes[i].equals("bcc")) {
                                if (bcc == null)
                                    bcc = xemail;
                                else
                                    bcc += "; " + xemail;
                            }

                            listdone = true;
                            checkemail = false;
                        }
                    }
                }
            }
            if (listdone) {
                continue;
            }

            if (checkemail) {
                try {
                    checkEmail(email);
                    //InternetAddress.parse(email.replace(',', ' '), false);
                    getInternetAddress(email);
                } catch (AddressException exc) {
                    Service.logger.error("Exception", exc);
                    throw new AddressException(lookupResource(MailLocaleKey.ADDRESS_ERROR) + " : " + email);
                }
            }

            if (rtypes[i].equals("to")) {
                if (to == null) {
                    to = email;
                } else {
                    to += "; " + email;
                }
            } else if (rtypes[i].equals("cc")) {
                if (cc == null) {
                    cc = email;
                } else {
                    cc += "; " + email;
                }
            } else if (rtypes[i].equals("bcc")) {
                if (bcc == null) {
                    bcc = email;
                } else {
                    bcc += "; " + email;
                }
            }
        }

        //long id = Long.parseLong(msgid);
        SimpleMessage msg = new SimpleMessage(msgId);
        /*int idx = jsmsg.identity - 1;
        Identity from = null;
        if (idx >= 0) {
           from = mprofile.getIdentity(idx);
        }*/
        Identity from = mprofile.getIdentity(jsmsg.identityId);
        msg.setFrom(from);
        msg.setTo(to);
        msg.setCc(cc);
        msg.setBcc(bcc);
        msg.setSubject(jsmsg.subject);

        //TODO: fax coverpage - dismissed
        /*if (isFax) {
           String coverpage = request.getParameter("faxcover");
           if (coverpage != null) {
        if (coverpage.equals("none")) {
           msg.addHeaderLine("X-FAX-AutoCoverPage: No");
        } else {
           msg.addHeaderLine("X-FAX-AutoCoverPage: Yes");
           msg.addHeaderLine("X-FAX-Cover-Template: " + coverpage);
        }
           }
        }*/

        //TODO: custom headers keys
        /*String[] headersKeys = request.getParameterValues("headersKeys");
        String[] headersValues = request.getParameterValues("headersValues");
        if (headersKeys != null && headersValues != null && headersKeys.length == headersValues.length) {
           for (int i = 0; i < headersKeys.length; i++) {
        if (!headersKeys[i].equals("")) {
           msg.addHeaderLine(headersKeys[i] + ": " + headersValues[i]);
        }
           }
        }*/

        if (jsmsg.inreplyto != null) {
            msg.setInReplyTo(jsmsg.inreplyto);
        }
        if (jsmsg.references != null) {
            msg.setReferences(new String[] { jsmsg.references });
        }
        if (jsmsg.replyfolder != null) {
            msg.setReplyFolder(jsmsg.replyfolder);
        }

        if (jsmsg.forwardedfolder != null) {
            msg.setForwardedFolder(jsmsg.forwardedfolder);
        }
        if (jsmsg.forwardedfrom != null) {
            msg.setForwardedFrom(jsmsg.forwardedfrom);
        }

        msg.setReceipt(jsmsg.receipt);
        msg.setPriority(jsmsg.priority ? 1 : 3);
        if (jsmsg.format == null || jsmsg.format.equals("plain")) {
            msg.setContent(jsmsg.content);
        } else {
            if (jsmsg.format.equalsIgnoreCase("html")) {
                //TODO: change this weird matching of cids2urls!

                //CIDs
                String content = jsmsg.content;
                String pattern1 = RegexUtils
                        .escapeRegexSpecialChars("service-request?csrf=" + getEnv().getCSRFToken() + "&amp;service="
                                + SERVICE_ID + "&amp;action=PreviewAttachment&amp;nowriter=true&amp;uploadId=");
                String pattern2 = RegexUtils.escapeRegexSpecialChars("&amp;cid=");
                content = StringUtils.replacePattern(content, pattern1 + ".{39}" + pattern2, "cid:");
                pattern1 = RegexUtils.escapeRegexSpecialChars("service-request?csrf=" + getEnv().getCSRFToken()
                        + "&service=" + SERVICE_ID + "&action=PreviewAttachment&nowriter=true&uploadId=");
                pattern2 = RegexUtils.escapeRegexSpecialChars("&cid=");
                content = StringUtils.replacePattern(content, pattern1 + ".{39}" + pattern2, "cid:");

                //URLs
                pattern1 = RegexUtils
                        .escapeRegexSpecialChars("service-request?csrf=" + getEnv().getCSRFToken() + "&amp;service="
                                + SERVICE_ID + "&amp;action=PreviewAttachment&amp;nowriter=true&amp;uploadId=");
                pattern2 = RegexUtils.escapeRegexSpecialChars("&amp;url=");
                content = StringUtils.replacePattern(content, pattern1 + ".{39}" + pattern2, "");
                pattern1 = RegexUtils.escapeRegexSpecialChars("service-request?csrf=" + getEnv().getCSRFToken()
                        + "&service=" + SERVICE_ID + "&action=PreviewAttachment&nowriter=true&uploadId=");
                pattern2 = RegexUtils.escapeRegexSpecialChars("&url=");
                content = StringUtils.replacePattern(content, pattern1 + ".{39}" + pattern2, "");

                //My resources as cids?
                if (ss.isPublicResourceLinksAsInlineAttachments()) {
                    ArrayList<JsAttachment> rescids = new ArrayList<>();
                    String match = "\"" + URIUtils.concat(getEnv().getCoreServiceSettings().getPublicBaseUrl(),
                            ResourceRequest.URL);
                    while (StringUtils.contains(content, match)) {
                        pattern1 = RegexUtils.escapeRegexSpecialChars(match);
                        Pattern pattern = Pattern.compile(pattern1 + "\\S*");
                        Matcher matcher = pattern.matcher(content);
                        matcher.find();
                        String matched = matcher.group();
                        String url = matched.substring(1, matched.length() - 1);
                        URI uri = new URI(url);

                        // Retrieve macthed URL 
                        // and save it locally
                        logger.debug("Downloading resource file as uploaded file from URL [{}]", url);
                        HttpClient httpCli = null;
                        try {
                            httpCli = HttpClientUtils.createBasicHttpClient(HttpClientUtils.configureSSLAcceptAll(),
                                    uri);
                            InputStream is = HttpClientUtils.getContent(httpCli, uri);
                            String tag = "" + msgId;
                            String filename = PathUtils.getFileName(uri.getPath());
                            UploadedFile ufile = addAsUploadedFile(tag, filename,
                                    ServletHelper.guessMediaType(filename), is);
                            rescids.add(new JsAttachment(ufile.getUploadId(), filename, ufile.getUploadId(), true,
                                    ufile.getSize()));
                            content = matcher.replaceFirst("\"cid:" + ufile.getUploadId() + "\"");
                        } catch (IOException ex) {
                            throw new WTException(ex, "Unable to retrieve webcal [{0}]", uri);
                        } finally {
                            HttpClientUtils.closeQuietly(httpCli);
                        }
                    }

                    //add new resource cids as attachments
                    if (rescids.size() > 0) {
                        if (jsmsg.attachments == null)
                            jsmsg.attachments = new ArrayList<>();
                        jsmsg.attachments.addAll(rescids);
                    }
                }

                String textcontent = MailUtils.HtmlToText_convert(MailUtils.htmlunescapesource(content));
                String htmlcontent = MailUtils.htmlescapefixsource(content).trim();
                if (htmlcontent.length() < 6 || !htmlcontent.substring(0, 6).toLowerCase().equals("<html>")) {
                    htmlcontent = "<html><header></header><body>" + htmlcontent + "</body></html>";
                }
                msg.setContent(htmlcontent, textcontent, "text/html");
            } else {
                msg.setContent(jsmsg.content, null, "text/" + jsmsg.format);
            }

        }
        return msg;
    }

    private void checkEmail(String email) throws AddressException {
        int ix = email.indexOf('@');
        if (ix < 1) {
            throw new AddressException(email);
        }
        int ix2 = email.indexOf('@', ix + 1);
        if (ix2 >= 0) {
            int ixx = email.indexOf('<');
            if (ixx >= 0) {
                if (!(ix < ixx && ix2 > ixx)) {
                    throw new AddressException(email);
                }
            } else {
                throw new AddressException(email);
            }
        }
    }

    public void processAttachFromMail(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        try {
            MailAccount account = getAccount(request);
            account.checkStoreConnected();

            String tag = request.getParameter("tag");
            String pfoldername = request.getParameter("folder");
            String puidmessage = request.getParameter("idmessage");
            String pidattach = request.getParameter("idattach");

            FolderCache mcache = account.getFolderCache(pfoldername);
            long uidmessage = Long.parseLong(puidmessage);
            Message m = mcache.getMessage(uidmessage);
            HTMLMailData mailData = mcache.getMailData((MimeMessage) m);
            Part part = mailData.getAttachmentPart(Integer.parseInt(pidattach));

            String ctype = part.getContentType();
            int ix = ctype.indexOf(";");
            if (ix > 0) {
                ctype = ctype.substring(0, ix);
            }

            String filename = part.getFileName();
            if (filename == null) {
                filename = "";
            }
            try {
                filename = MailUtils.decodeQString(filename);
            } catch (Exception exc) {
            }

            ctype = ServletHelper.guessMediaType(filename, ctype);

            File file = WT.createTempFile();
            int filesize = IOUtils.copy(part.getInputStream(), new FileOutputStream(file));
            WebTopSession.UploadedFile uploadedFile = new WebTopSession.UploadedFile(false, this.SERVICE_ID,
                    file.getName(), tag, filename, filesize, ctype);
            environment.getSession().addUploadedFile(uploadedFile);

            MapItem data = new MapItem(); // Empty response data
            data.add("uploadId", uploadedFile.getUploadId());
            data.add("name", uploadedFile.getFilename());
            data.add("size", uploadedFile.getSize());
            data.add("editable", isFileEditableInDocEditor(filename));
            new JsonResult(data).printTo(out);
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            new JsonResult(false, exc.getMessage()).printTo(out);
        }
    }

    public void processAttachFromMessages(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        try {
            MailAccount account = getAccount(request);
            account.checkStoreConnected();

            String tag = request.getParameter("tag");
            String pfoldername = request.getParameter("folder");
            String suids[] = request.getParameterValues("ids");
            String multifolder = request.getParameter("multifolder");
            boolean mf = multifolder != null && multifolder.equals("true");

            String ctype = "message/rfc822";

            ArrayList<WebTopSession.UploadedFile> ufiles = new ArrayList<>();
            for (String suid : suids) {
                String foldername = pfoldername;

                if (mf) {
                    int ix = suid.indexOf("|");
                    foldername = suid.substring(0, ix);
                    suid = suid.substring(ix + 1);
                }
                long uid = Long.parseLong(suid);
                FolderCache mcache = account.getFolderCache(foldername);

                Message msg = mcache.getMessage(uid);
                File file = WT.createTempFile();
                FileOutputStream fos = new FileOutputStream(file);
                msg.writeTo(fos);
                fos.close();
                long filesize = file.length();
                String filename = msg.getSubject() + ".eml";

                WebTopSession.UploadedFile uploadedFile = new WebTopSession.UploadedFile(false, this.SERVICE_ID,
                        file.getName(), tag, filename, filesize, ctype);
                environment.getSession().addUploadedFile(uploadedFile);
                ufiles.add(uploadedFile);
            }

            MapItemList data = new MapItemList();
            for (WebTopSession.UploadedFile ufile : ufiles) {
                MapItem mi = new MapItem();
                mi.add("uploadId", ufile.getUploadId());
                mi.add("name", ufile.getFilename());
                mi.add("size", ufile.getSize());
                mi.add("editable", isFileEditableInDocEditor(ufile.getFilename()));
                data.add(mi);
            }
            new JsonResult(data).printTo(out);
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            new JsonResult(false, exc.getMessage()).printTo(out);
        }
    }

    public void processAttachFromCloud(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        try {
            int storeId = ServletUtils.getIntParameter(request, "storeId", true);
            String path = ServletUtils.getStringParameter(request, "path", true);
            String tag = ServletUtils.getStringParameter(request, "tag", true);

            WebTopSession.UploadedFile uploadedFile = null;
            FileObject fo = vfsmanager.getStoreFile(storeId, path);
            if (fo == null)
                throw new WTException("Unable to get file [{}, {}]", storeId, path);
            InputStream is = null;
            try {
                is = fo.getContent().getInputStream();
                String name = fo.getName().getBaseName();
                String mediaType = ServletHelper.guessMediaType(name, true);
                uploadedFile = addAsUploadedFile(tag, name, mediaType, is);
            } finally {
                IOUtils.closeQuietly(is);
            }
            if (uploadedFile == null)
                throw new WTException("Unable to prepare uploaded file");

            MapItem data = new MapItem();
            data.add("uploadId", uploadedFile.getUploadId());
            data.add("name", uploadedFile.getFilename());
            data.add("size", uploadedFile.getSize());
            new JsonResult(data).printTo(out);

        } catch (FileSystemException | WTException ex) {
            Service.logger.error("Exception", ex);
            new JsonResult(false, ex.getMessage()).printTo(out);
        } catch (Exception ex) {
            Service.logger.error("Exception", ex);
            new JsonResult(false, ex.getMessage()).printTo(out);
        }
    }

    public void processSaveFileToCloud(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        try {
            MailAccount account = getAccount(request);
            String path = ServletUtils.getStringParameter(request, "path", true);
            String fileId = ServletUtils.getStringParameter(request, "fileId", true);
            int storeId = ServletUtils.getIntParameter(request, "storeId", true);
            String folder = ServletUtils.getStringParameter(request, "folder", true);
            int idAttach = ServletUtils.getIntParameter(request, "idAttach", true);
            int idMessage = ServletUtils.getIntParameter(request, "idMessage", true);

            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(folder);
            Message m = mcache.getMessage(idMessage);
            HTMLMailData mailData = mcache.getMailData((MimeMessage) m);
            Part part = mailData.getAttachmentPart(idAttach);
            String fileName = part.getFileName();
            InputStream is = part.getInputStream();

            vfsmanager.addStoreFileFromStream(storeId, path, fileName, is);

            MapItem data = new MapItem();
            data.add("success", true);
            new JsonResult(data).printTo(out);
        } catch (Exception ex) {
            Service.logger.error("Exception", ex);
            new JsonResult(false, ex.getMessage()).printTo(out);
        }

    }

    public void processCopyAttachment(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        try {
            MailAccount fromaccount = getAccount(request.getParameter("fromaccount"));
            MailAccount toaccount = getAccount(request.getParameter("toaccount"));
            fromaccount.checkStoreConnected();
            toaccount.checkStoreConnected();
            UserProfile profile = environment.getProfile();
            Locale locale = profile.getLocale();

            String pfromfolder = request.getParameter("fromfolder");
            String ptofolder = request.getParameter("tofolder");
            String puidmessage = request.getParameter("idmessage");
            String pidattach = request.getParameter("idattach");

            FolderCache frommcache = fromaccount.getFolderCache(pfromfolder);
            FolderCache tomcache = toaccount.getFolderCache(ptofolder);
            long uidmessage = Long.parseLong(puidmessage);
            Message m = frommcache.getMessage(uidmessage);

            HTMLMailData mailData = frommcache.getMailData((MimeMessage) m);
            Object content = mailData.getAttachmentPart(Integer.parseInt(pidattach)).getContent();

            // We can copy attachments only if the content is an eml. If it is
            // explicit simply treat it as message, otherwise try to decode the
            // stream as a mime message. If an error is thrown during parse, it 
            // means that the stream is reconducible to a valid mime-message.
            MimeMessage msgContent = null;
            if (content instanceof MimeMessage) {
                msgContent = new MimeMessage((MimeMessage) content);
            } else if (content instanceof IMAPInputStream) {
                try {
                    msgContent = new MimeMessage(fromaccount.getMailSession(), (IMAPInputStream) content);
                } catch (MessagingException ex1) {
                    logger.debug("Stream cannot be interpreted as MimeMessage", ex1);
                }
            }

            if (msgContent != null) {
                msgContent.setFlag(Flags.Flag.SEEN, true);
                tomcache.appendMessage(msgContent);
                new JsonResult().printTo(out);
            } else {
                new JsonResult(false, lookupResource(locale, MailLocaleKey.ERROR_ATTACHMENT_TYPE_NOT_SUPPORTED))
                        .printTo(out);
            }

        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            new JsonResult(false, exc.getMessage()).printTo(out);
        }
    }

    public void processSendReceipt(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        UserProfile profile = environment.getProfile();
        String subject = request.getParameter("subject");
        //String from = request.getParameter("from");
        int identityId = Integer.parseInt(request.getParameter("identityId"));
        String to = request.getParameter("to");
        //String folder = request.getParameter("folder");
        //String sout = "";
        Identity ident = mprofile.getIdentity(identityId);
        String from = ident.getDisplayName() + " <" + ident.getEmail() + ">";

        String body = "Il messaggio inviato a " + from + " con soggetto [" + subject + "]  stato letto.\n\n"
                + "Your message sent to " + from + " with subject [" + subject + "] has been read.\n\n";
        try {
            account.checkStoreConnected();
            Exception exc = sendReceipt(ident, from, to, subject, body);
            if (exc == null) {
                new JsonResult().printTo(out);
            } else {
                Service.logger.error("Exception", exc);
                new JsonResult(exc).printTo(out);
            }
        } catch (MessagingException exc) {
            Service.logger.error("Exception", exc);
            new JsonResult(exc).printTo(out);
        }
    }

    public void processPortletMail(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        ArrayList<JsPreviewMessage> items = new ArrayList<>();

        try {
            MailAccount account = getAccount(request);
            String query = ServletUtils.getStringParameter(request, "query", null);
            int visibleRows = 0;
            int maxVisibleRows = 20;

            if (query == null) {
                String folderId = account.getInboxFolderFullName();
                FolderCache fc = account.getFolderCache(folderId);
                Message msgs[] = fc.getMessages(FolderCache.SORT_BY_DATE, false, true, -1, true, false, null,
                        false);
                if (msgs != null)
                    fc.fetch(msgs, getMessageFetchProfile(), 0, 50);
                else
                    msgs = new Message[0];

                for (Message msg : msgs) {
                    SonicleIMAPMessage simsg = (SonicleIMAPMessage) msg;

                    InternetAddress iafrom = null;
                    Address vfrom[] = msg.getFrom();
                    if (vfrom != null && vfrom.length > 0) {
                        Address afrom = vfrom[0];
                        if (afrom instanceof InternetAddress) {
                            iafrom = (InternetAddress) afrom;
                        }
                    }

                    Address[] rcpts = msg.getRecipients(Message.RecipientType.TO);
                    ArrayList<InternetAddress> tos = new ArrayList<>();
                    if (rcpts != null)
                        for (Address ato : rcpts) {
                            if (ato instanceof InternetAddress) {
                                InternetAddress iato = (InternetAddress) ato;
                                tos.add(iato);
                            }
                        }

                    String msgtext = "";
                    if (visibleRows < maxVisibleRows) {
                        msgtext = MailUtils.peekText(simsg);
                        if (msgtext == null)
                            msgtext = "";
                        else {
                            msgtext = msgtext.trim();
                            if (msgtext.length() > 100)
                                msgtext = msgtext.substring(0, 100);
                        }
                        ++visibleRows;
                    }

                    String from = iafrom != null
                            ? (iafrom.getPersonal() != null ? iafrom.getPersonal() : iafrom.getAddress())
                            : "";
                    String to = "";
                    if (tos.size() > 0) {
                        boolean first = true;
                        for (InternetAddress iato : tos) {
                            if (!first)
                                to += "; ";
                            to += (iato.getPersonal() != null ? iato.getPersonal() : iato.getAddress());
                            first = false;
                        }
                    }

                    JsPreviewMessage jsmsg = new JsPreviewMessage(simsg.getUID(), folderId,
                            getInternationalFolderName(account.getFolderCache(folderId)), simsg.getSubject(), from,
                            to, msg.getReceivedDate(), msgtext);
                    items.add(jsmsg);
                }
            } else {
            }

            new JsonResult(items).printTo(out);

        } catch (Exception ex) {
            logger.error("Error in PortletMail", ex);
            new JsonResult(false, "Error").printTo(out);
        }
    }

    public void processManageQuickParts(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        String crud = null;
        HashMap<String, String> items;
        try {
            crud = ServletUtils.getStringParameter(request, "crud", true);
            if (crud.equals(Crud.READ)) {
                items = us.getMessageQuickParts();
                new JsonResult(JsQuickPart.asList(items)).printTo(out);

            } else if (crud.equals(Crud.CREATE)) {
                String id = ServletUtils.getStringParameter(request, "id", true);
                String html = ServletUtils.getStringParameter(request, "html", true);
                us.setMessageQuickPart(id, html);

                items = us.getMessageQuickParts();
                new JsonResult(JsQuickPart.asList(items)).printTo(out);

            } else if (crud.equals(Crud.DELETE)) {
                Payload<MapItem, JsQuickPart> pl = ServletUtils.getPayload(request, JsQuickPart.class);
                us.deleteMessageQuickPart(pl.data.id);

                new JsonResult().printTo(out);
            }
        } catch (Exception ex) {
            logger.error("Error managing quickparts", ex);
            new JsonResult(false, "Error managing quickparts").printTo(out);
        }
    }

    public void processManageTags(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        String crud = null;
        List<JsTag> items;
        try {
            MailAccount account = getAccount(request);
            crud = ServletUtils.getStringParameter(request, "crud", true);
            if (crud.equals(Crud.READ)) {
                items = new ArrayList<>();
                for (Tag t : atags)
                    items.add(new JsTag(t.getTagId(), t.getDescription(), t.getColor()));
                new JsonResult(items).printTo(out);
            } else if (crud.equals(Crud.CREATE)) {
                Payload<MapItem, JsTag> pl = ServletUtils.getPayload(request, JsTag.class);
                Tag tag = new Tag(pl.data.tagId, pl.data.description, pl.data.color);
                tag.setTagId(mailManager.sanitazeTagId(tag.getTagId()));
                mailManager.addTag(tag);
                loadTags();

                items = new ArrayList<>();
                items.add(new JsTag(tag.getTagId(), tag.getDescription(), tag.getColor()));
                new JsonResult(items).printTo(out);
            } else if (crud.equals(Crud.UPDATE)) {
                Payload<MapItem, JsTag> pl = ServletUtils.getPayload(request, JsTag.class);
                Tag tag = new Tag(pl.data.tagId, pl.data.description, pl.data.color);
                String newTagId = tag.getDescription();
                String oldTagId = tag.getTagId();
                newTagId = mailManager.sanitazeTagId(newTagId);
                mailManager.updateTag(tag, newTagId);
                mailManager.updateFoldersTag(oldTagId, newTagId, account.getFolderCacheValues(), null, false);
                loadTags();

                items = new ArrayList<>();
                items.add(new JsTag(tag.getTagId(), tag.getDescription(), tag.getColor()));
                new JsonResult(items).printTo(out);
            } else if (crud.equals(Crud.DELETE)) {
                Payload<MapItem, JsTag> pl = ServletUtils.getPayload(request, JsTag.class);
                mailManager.removeTag(pl.data.tagId);
                loadTags();

                new JsonResult().printTo(out);
            }
        } catch (Exception ex) {
            logger.error("Error managing tags", ex);
            new JsonResult(false, "Error managing tags").printTo(out);
        }
    }

    public void processTagMessages(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String fromfolder = request.getParameter("fromfolder");
        String uids[] = request.getParameterValues("ids");
        String tagId = request.getParameter("tagId");
        String multifolder = request.getParameter("multifolder");
        boolean mf = multifolder != null && multifolder.equals("true");
        try {
            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(fromfolder);
            if (!mf) {
                tagMessages(mcache, toLongs(uids), tagId);
            } else {
                long iuids[] = new long[1];
                for (String uid : uids) {
                    int ix = uid.indexOf("|");
                    fromfolder = uid.substring(0, ix);
                    uid = uid.substring(ix + 1);
                    mcache = account.getFolderCache(fromfolder);
                    iuids[0] = Long.parseLong(uid);
                    tagMessages(mcache, iuids, tagId);
                }
            }
            new JsonResult().printTo(out);
        } catch (MessagingException exc) {
            logger.error("Error managing tags", exc);
            new JsonResult(false, lookupResource(MailLocaleKey.PERMISSION_DENIED)).printTo(out);
        }
    }

    public void processUntagMessages(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String fromfolder = request.getParameter("fromfolder");
        String uids[] = request.getParameterValues("ids");
        String tagId = request.getParameter("tagId");
        String multifolder = request.getParameter("multifolder");
        boolean mf = multifolder != null && multifolder.equals("true");
        try {
            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(fromfolder);
            if (!mf) {
                untagMessages(mcache, toLongs(uids), tagId);
            } else {
                long iuids[] = new long[1];
                for (String uid : uids) {
                    int ix = uid.indexOf("|");
                    fromfolder = uid.substring(0, ix);
                    uid = uid.substring(ix + 1);
                    mcache = account.getFolderCache(fromfolder);
                    iuids[0] = Long.parseLong(uid);
                    untagMessages(mcache, iuids, tagId);
                }
            }
            new JsonResult().printTo(out);
        } catch (MessagingException exc) {
            logger.error("Error managing tags", exc);
            new JsonResult(false, "Error managing tags").printTo(out);
        }
    }

    public void processClearMessagesTags(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        MailAccount account = getAccount(request);
        String fromfolder = request.getParameter("fromfolder");
        String uids[] = request.getParameterValues("ids");
        String multifolder = request.getParameter("multifolder");
        boolean mf = multifolder != null && multifolder.equals("true");
        try {
            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(fromfolder);
            if (!mf) {
                clearMessagesTags(mcache, toLongs(uids));
            } else {
                long iuids[] = new long[1];
                for (String uid : uids) {
                    int ix = uid.indexOf("|");
                    fromfolder = uid.substring(0, ix);
                    uid = uid.substring(ix + 1);
                    mcache = account.getFolderCache(fromfolder);
                    iuids[0] = Long.parseLong(uid);
                    clearMessagesTags(mcache, iuids);
                }
            }
            new JsonResult().printTo(out);
        } catch (MessagingException exc) {
            logger.error("Error managing tags", exc);
            new JsonResult(false, "Error managing tags").printTo(out);
        }
    }

    /*   public void processListPublicImages(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
          SettingsManager sm = wta.getSettingsManager();
          ArrayList<JsonHashMap> items = null;
          JsonHashMap item = null;
              
          try {
     items = new ArrayList<JsonHashMap>();
     String publicArea = sm.getSetting("webtop.public");
     File pubimgDir = new File(publicArea + "/main/images/");
     if(pubimgDir.isDirectory() && pubimgDir.exists()) {
        File[] files = pubimgDir.listFiles();
        for(File file : files) {
           String url = "webtop/public/images/"+file.getName();
           item = new JsonHashMap(url, file.getName());
           items.add(item);
        }
     }
     new JsonResult(items).printTo(out);
         
          } catch (Exception ex) {
     logger.error("Error ListPublicImages", ex);
     new JsonResult(false, "Error ListPublicImages").printTo(out);
          }
       }   */

    private String getSharedFolderName(MailAccount account, String mailUser, String folder)
            throws MessagingException {
        FolderCache folderCache = null;
        String sharedFolderName = null;
        String folderName = null;

        // Clear mailUser removing any domain info (ldap auth contains 
        // domain suffix), we don't want it!
        String user = StringUtils.split(mailUser, "@")[0];
        // INBOX is a fake name, it's equals to user's direct folder
        boolean isInbox = folder.equals("INBOX");

        FolderCache[] sharedCache = account.getSharedFoldersCache();
        for (FolderCache sharedFolder : sharedCache) {
            sharedFolderName = sharedFolder.getFolderName();
            folderCache = account.getFolderCache(sharedFolderName);
            for (Folder fo : folderCache.getFolder().list()) {
                folderName = fo.getFullName();
                char sep = fo.getSeparator();
                //if is a shared mailbox, and it contains an @, match it with mail user (NS7)
                //or just user instead (XStream and NS6)
                String name = isInbox ? (fo.getName().indexOf('@') > 0 ? mailUser : user) : folder;
                if (folderName.equals(sharedFolderName + sep + name))
                    return folderName;
            }
        }
        return null;
    }

    HashMap<String, MessageListThread> mlThreads = new HashMap<String, MessageListThread>();

    public void processGroupChanged(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        String group = request.getParameter("group");
        String folder = request.getParameter("folder");
        us.setMessageListGroup(folder, group);
        if (!group.equals(""))
            us.setMessageListSort(folder, "date|DESC");
        new JsonResult(true).printTo(out);
    }

    public void processSavePageRows(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        String spagerows = request.getParameter("pagerows");
        int pagerows = Integer.parseInt(spagerows);
        us.setPageRows(pagerows);
        mprofile.setNumMsgList(pagerows);
        new JsonResult(true, "").printTo(out);
    }

    public void processUploadToFolder(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        try {
            MailAccount account = getAccount(request);
            String currentFolder = request.getParameter("folder");
            String uploadId = request.getParameter("uploadId");

            UploadedFile upfile = getUploadedFile(uploadId);
            InputStream in = new FileInputStream(upfile.getFile());
            MimeMessage msgContent = new MimeMessage(account.getMailSession(), in);
            FolderCache tomcache = account.getFolderCache(currentFolder);
            msgContent.setFlag(Flags.Flag.SEEN, true);
            tomcache.appendMessage(msgContent);
            new JsonResult().printTo(out);
        } catch (Exception exc) {
            logger.debug("Cannot upload to folder", exc);
            new JsonResult("Cannot upload to folder", exc).printTo(out);
        }
    }

    class MessagesInfo {
        Message messages[];
        long millis;
        private MessageListThread mlt;

        MessagesInfo(Message msgs[], MessageListThread mlt) {
            this.messages = msgs;
            this.millis = mlt.millis;
            this.mlt = mlt;
        }

        public boolean isPEC() {
            return mlt.isPec;
        }

        /*      public boolean checkSkipPEC(Message xm) throws MessagingException {
                 return mlt.checkSkipPEC(xm);
              }*/
    }

    private MessagesInfo listMessages(FolderCache mcache, String key, boolean refresh, SortGroupInfo sgi,
            long timestamp, SearchTerm searchTerm, boolean hasAttachment) throws MessagingException {
        MessageListThread mlt = null;
        synchronized (mlThreads) {
            mlt = mlThreads.get(key);
            if (mlt == null || (mlt.lastRequest != timestamp && refresh)) {
                //if (mlt!=null)
                //   System.out.println(page+": same time stamp ="+(mlt.lastRequest!=timestamp)+" - refresh = "+refresh);
                //else
                //   System.out.println(page+": mlt not found");
                mlt = new MessageListThread(mcache, sgi.sortby, sgi.sortascending, refresh, sgi.sortgroup,
                        sgi.groupascending, sgi.threaded, searchTerm, hasAttachment);
                mlt.lastRequest = timestamp;
                mlThreads.put(key, mlt);
            }
            //else System.out.println(page+": reusing list thread");

            //remove old requests
            ArrayList<String> rkeys = null;
            for (String xkey : mlThreads.keySet()) {
                MessageListThread xmlt = mlThreads.get(xkey);
                //remove if older than one minute
                long age = timestamp - xmlt.lastRequest;
                if (xmlt != null && age > 60000) {
                    if (rkeys == null)
                        rkeys = new ArrayList<>();
                    rkeys.add(xkey);
                }
            }
            if (rkeys != null) {
                for (String xkey : rkeys) {
                    //MessageListThread xmlt = mlThreads.get(xkey);
                    //long age=timestamp - xmlt.lastRequest;
                    //System.out.println("removing ["+xkey+"] - age: "+age);
                    mlThreads.remove(xkey);
                }
            }

            //System.out.println("mlThreads size is now "+mlThreads.size());
        }

        Message xmsgs[] = null;
        synchronized (mlt.lock) {
            if (!mlt.started) {
                //System.out.println(page+": starting list thread");
                Thread t = new Thread(mlt);
                t.start();
            }
            if (!mlt.finished) {
                //System.out.println(page+": waiting list thread to finish");
                try {
                    mlt.lock.wait();
                } catch (InterruptedException exc) {
                    Service.logger.error("Exception", exc);
                }
                //mlThreads.remove(key);
            }
            //TODO: see if we can check first request from buffered store
            //if (mlt.lastRequest==timestamp) {
            //System.out.println(page+": got list thread result");
            xmsgs = mlt.msgs;
            //}
        }

        return new MessagesInfo(xmsgs, mlt);
    }

    class SortGroupInfo {
        int sortby;
        boolean sortascending;
        int sortgroup;
        boolean groupascending;
        boolean threaded;

        SortGroupInfo(int sortby, boolean sortascending, int sortgroup, boolean groupascending, boolean threaded) {
            this.sortby = sortby;
            this.sortascending = sortascending;
            this.sortgroup = sortgroup;
            this.groupascending = groupascending;
            this.threaded = threaded;
        }
    }

    private SortGroupInfo getSortGroupInfo(String psortfield, String psortdir, String group) {
        int sortby = MessageComparator.SORT_BY_NONE;
        if (psortfield.equals("messageid")) {
            sortby = MessageComparator.SORT_BY_MSGIDX;
        } else if (psortfield.equals("date")) {
            sortby = MessageComparator.SORT_BY_DATE;
        } else if (psortfield.equals("priority")) {
            sortby = MessageComparator.SORT_BY_PRIORITY;
        } else if (psortfield.equals("to")) {
            sortby = MessageComparator.SORT_BY_RCPT;
        } else if (psortfield.equals("from")) {
            sortby = MessageComparator.SORT_BY_SENDER;
        } else if (psortfield.equals("size")) {
            sortby = MessageComparator.SORT_BY_SIZE;
        } else if (psortfield.equals("subject")) {
            sortby = MessageComparator.SORT_BY_SUBJECT;
        } else if (psortfield.equals("status") || psortfield.equals("unread")) {
            sortby = MessageComparator.SORT_BY_STATUS;
        } else if (psortfield.equals("flag")) {
            sortby = MessageComparator.SORT_BY_FLAG;
        }

        int sortgroup = MessageComparator.SORT_BY_NONE;
        boolean groupascending = true;
        if (group.equals("messageid")) {
            sortgroup = MessageComparator.SORT_BY_MSGIDX;
        } else if (group.equals("gdate")) {
            sortgroup = MessageComparator.SORT_BY_DATE;
            groupascending = false;
        } else if (group.equals("priority")) {
            sortgroup = MessageComparator.SORT_BY_PRIORITY;
        } else if (group.equals("to")) {
            sortgroup = MessageComparator.SORT_BY_RCPT;
        } else if (group.equals("from")) {
            sortgroup = MessageComparator.SORT_BY_SENDER;
        } else if (group.equals("size")) {
            sortgroup = MessageComparator.SORT_BY_SIZE;
        } else if (group.equals("subject")) {
            sortgroup = MessageComparator.SORT_BY_SUBJECT;
        } else if (group.equals("status")) {
            sortgroup = MessageComparator.SORT_BY_STATUS;
        } else if (group.equals("flag")) {
            sortgroup = MessageComparator.SORT_BY_FLAG;
        }

        boolean threaded = group.equals("threadId");

        if (threaded && (!psortfield.equals("date") || !psortdir.equals("DESC")))
            threaded = false;

        return new SortGroupInfo(sortby, psortdir.equals("ASC"), sortgroup, groupascending, threaded);
    }

    public void processListMessages(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        CoreManager core = WT.getCoreManager();
        UserProfile profile = environment.getProfile();
        Locale locale = profile.getLocale();
        java.util.Calendar cal = java.util.Calendar.getInstance(locale);
        MailAccount account = getAccount(request);
        String pfoldername = request.getParameter("folder");
        //String psortfield = request.getParameter("sort");
        //String psortdir = request.getParameter("dir");
        String pstart = request.getParameter("start");
        String plimit = request.getParameter("limit");
        String ppage = request.getParameter("page");
        String prefresh = request.getParameter("refresh");
        String ptimestamp = request.getParameter("timestamp");
        String pthreaded = request.getParameter("threaded");
        String pthreadaction = request.getParameter("threadaction");
        String pthreadactionuid = request.getParameter("threadactionuid");

        QueryObj queryObj = null;
        SearchTerm searchTerm = null;
        try {
            queryObj = ServletUtils.getObjectParameter(request, "query", new QueryObj(), QueryObj.class);
        }

        catch (ParameterException parameterException) {
            logger.error("Exception getting query obejct parameter", parameterException);
        }

        boolean refresh = (prefresh != null && prefresh.equals("true"));
        //boolean threaded=(pthreaded!=null && pthreaded.equals("1"));

        //String threadedSetting="list-threaded-"+pfoldername;
        //if (pthreaded==null || pthreaded.equals("2")) {
        //   threaded=us.isMessageListThreaded(pfoldername);
        //} else {
        //   us.setMessageListThreaded(pfoldername, threaded);
        //}
        //System.out.println("timestamp="+ptimestamp);
        long timestamp = Long.parseLong(ptimestamp);

        if (account.isSpecialFolder(pfoldername) || account.isSharedFolder(pfoldername)) {
            logger.debug("folder is special or shared, refresh forced");
            refresh = true;
        }

        String group = us.getMessageListGroup(pfoldername);
        if (group == null) {
            group = "";
        }

        String psortfield = "date";
        String psortdir = "DESC";
        try {
            boolean nogroup = group.equals("");
            JsSort.List sortList = ServletUtils.getObjectParameter(request, "sort", null, JsSort.List.class);
            if (sortList == null) {
                if (nogroup) {
                    String s = us.getMessageListSort(pfoldername);
                    int ix = s.indexOf("|");
                    psortfield = s.substring(0, ix);
                    psortdir = s.substring(ix + 1);
                } else {
                    psortfield = "date";
                    psortdir = "DESC";
                }
            } else {
                JsSort jsSort = sortList.get(0);
                psortfield = jsSort.property;
                psortdir = jsSort.direction;
                if (!nogroup && !psortfield.equals("date")) {
                    group = "";
                }
                us.setMessageListGroup(pfoldername, group);
                us.setMessageListSort(pfoldername, psortfield, psortdir);
            }
        } catch (Exception exc) {
            logger.error("Exception", exc);
        }

        SortGroupInfo sgi = getSortGroupInfo(psortfield, psortdir, group);

        //Save search requests

        int start = Integer.parseInt(pstart);
        int limit = Integer.parseInt(plimit);
        int page = 0;
        if (ppage != null) {
            page = Integer.parseInt(ppage);
            start = (page - 1) * limit;
        }
        /*int start = 0;
        int limit = mprofile.getNumMsgList();
        if (ppage==null) {
           if (pstart != null) {
        start = Integer.parseInt(pstart);
           }   
           if (plimit != null) {
        limit = Integer.parseInt(plimit);
           }
        } else {
           int page=Integer.parseInt(ppage);
           int nxpage=mprofile.getNumMsgList();
           start=(page-1)*nxpage;
           limit=nxpage;
        }*/

        String sout = "{\n";
        Folder folder = null;
        boolean connected = false;
        try {
            connected = account.checkStoreConnected();
            if (!connected)
                throw new Exception("Mail account authentication error");

            int funread = 0;
            if (pfoldername == null) {
                folder = account.getDefaultFolder();
            } else {
                folder = account.getFolder(pfoldername);
            }
            boolean issent = account.isSentFolder(folder.getFullName());
            boolean isundersent = account.isUnderSentFolder(folder.getFullName());
            boolean isdrafts = account.isDraftsFolder(folder.getFullName());
            boolean isundershared = account.isUnderSharedFolder(pfoldername);
            if (!issent) {
                String names[] = folder.getFullName().split("\\" + account.getFolderSeparator());
                for (String pname : names) {
                    if (account.isSentFolder(pname)) {
                        issent = true;
                        break;
                    }
                }
            }

            String ctn = Thread.currentThread().getName();
            String key = folder.getFullName();

            if (!pfoldername.equals("/")) {

                FolderCache mcache = account.getFolderCache(key);
                if (mcache.toBeRefreshed())
                    refresh = true;
                //Message msgs[]=mcache.getMessages(ppattern,psearchfield,sortby,ascending,refresh);
                if (psortfield != null && psortdir != null) {
                    key += "|" + psortdir + "|" + psortfield;
                }

                searchTerm = ImapQuery.toSearchTerm(this.allFlagStrings, this.atags, queryObj,
                        profile.getTimeZone());

                boolean hasAttachment = queryObj.conditions.stream()
                        .anyMatch(condition -> condition.value.equals("attachment"));
                if (queryObj != null)
                    refresh = true;
                MessagesInfo messagesInfo = listMessages(mcache, key, refresh, sgi, timestamp, searchTerm,
                        hasAttachment);
                Message xmsgs[] = messagesInfo.messages;

                if (pthreadaction != null && pthreadaction.trim().length() > 0) {
                    long actuid = Long.parseLong(pthreadactionuid);
                    mcache.setThreadOpen(actuid, pthreadaction.equals("open"));
                }

                //if threaded, look for the start considering roots and opened children
                if (xmsgs != null && sgi.threaded && page > 1) {
                    int i = 0, ni = 0, np = 1;
                    long tId = 0;
                    while (np < page && ni < xmsgs.length) {
                        SonicleIMAPMessage xm = (SonicleIMAPMessage) xmsgs[ni];
                        ++ni;
                        if (xm.isExpunged())
                            continue;

                        long nuid = mcache.getUID(xm);

                        int tIndent = xm.getThreadIndent();
                        if (tIndent == 0)
                            tId = nuid;
                        else {
                            if (!mcache.isThreadOpen(tId))
                                continue;
                        }

                        ++i;
                        if ((i % limit) == 0)
                            ++np;
                    }
                    if (np == page) {
                        start = ni;
                        //System.out.println("page "+np+" start is "+start);
                    }
                }

                int max = start + limit;
                if (xmsgs != null && max > xmsgs.length)
                    max = xmsgs.length;
                ArrayList<Long> autoeditList = new ArrayList<Long>();
                if (xmsgs != null) {
                    int total = 0;
                    int expunged = 0;

                    //calculate expunged
                    //for(Message xmsg: xmsgs) {
                    //   if (xmsg.isExpunged()) ++expunged;
                    //}

                    sout += "messages: [\n";

                    /*               if (ppattern==null && !isSpecialFolder(mcache.getFolderName())) {
                     //mcache.fetch(msgs,FolderCache.flagsFP,0,start);
                     for(int i=0;i<start;++i) {
                     try {
                     if (!msgs[i].isSet(Flags.Flag.SEEN)) funread++;
                     } catch(Exception exc) {
                        
                     }
                     }
                     }*/
                    total = sgi.threaded ? mcache.getThreadedCount() : xmsgs.length;
                    if (start < max) {

                        Folder fsent = account.getFolder(account.getFolderSent());
                        boolean openedsent = false;
                        //Fetch others for these messages
                        mcache.fetch(xmsgs, (isdrafts ? draftsFP : messagesInfo.isPEC() ? pecFP : FP), start, max);
                        long tId = 0;
                        for (int i = 0, ni = 0; i < limit; ++ni, ++i) {
                            int ix = start + i;
                            int nx = start + ni;
                            if (nx >= xmsgs.length)
                                break;
                            if (ix >= max)
                                break;

                            SonicleIMAPMessage xm = (SonicleIMAPMessage) xmsgs[nx];
                            if (xm.isExpunged()) {
                                --i;
                                continue;
                            }

                            /*if (messagesInfo.checkSkipPEC(xm)) {
                               --i; --total;
                               continue;
                            }*/

                            /*String ids[]=null;
                             try {
                             ids=xm.getHeader("Message-ID");
                             } catch(MessagingException exc) {
                             --i;
                             continue;
                             }
                             if (ids==null || ids.length==0) { --i; continue; }
                             String idmessage=ids[0];*/

                            long nuid = mcache.getUID(xm);

                            int tIndent = xm.getThreadIndent();
                            if (tIndent == 0)
                                tId = nuid;
                            else if (sgi.threaded) {
                                if (!mcache.isThreadOpen(tId)) {
                                    --i;
                                    continue;
                                }
                            }
                            boolean tChildren = false;
                            int tUnseenChildren = 0;
                            if (sgi.threaded) {
                                int cnx = nx + 1;
                                while (cnx < xmsgs.length) {
                                    SonicleIMAPMessage cxm = (SonicleIMAPMessage) xmsgs[cnx];
                                    if (cxm.isExpunged()) {
                                        cnx++;
                                        continue;
                                    }
                                    while (cxm.getThreadIndent() > 0) {
                                        tChildren = true;
                                        if (!cxm.isExpunged() && !cxm.isSet(Flags.Flag.SEEN))
                                            ++tUnseenChildren;
                                        ++cnx;
                                        if (cnx >= xmsgs.length)
                                            break;
                                        cxm = (SonicleIMAPMessage) xmsgs[cnx];
                                    }
                                    break;
                                }
                            }

                            Flags flags = xm.getFlags();

                            //Date
                            java.util.Date d = xm.getSentDate();
                            if (d == null)
                                d = xm.getReceivedDate();
                            if (d == null)
                                d = new java.util.Date(0);
                            cal.setTime(d);
                            int yyyy = cal.get(java.util.Calendar.YEAR);
                            int mm = cal.get(java.util.Calendar.MONTH);
                            int dd = cal.get(java.util.Calendar.DAY_OF_MONTH);
                            int hhh = cal.get(java.util.Calendar.HOUR_OF_DAY);
                            int mmm = cal.get(java.util.Calendar.MINUTE);
                            int sss = cal.get(java.util.Calendar.SECOND);
                            //From
                            String from = "";
                            String fromemail = "";
                            Address ia[] = xm.getFrom();
                            if (ia != null) {
                                InternetAddress iafrom = (InternetAddress) ia[0];
                                from = iafrom.getPersonal();
                                if (from == null)
                                    from = iafrom.getAddress();
                                fromemail = iafrom.getAddress();
                            }
                            from = (from == null ? ""
                                    : StringEscapeUtils.escapeEcmaScript(MailUtils.htmlescape(from)));
                            fromemail = (fromemail == null ? ""
                                    : StringEscapeUtils.escapeEcmaScript(MailUtils.htmlescape(fromemail)));

                            //To
                            String to = "";
                            String toemail = "";
                            ia = xm.getRecipients(Message.RecipientType.TO);
                            //if not sent and not shared, show me first if in TO
                            if (ia != null) {
                                InternetAddress iato = (InternetAddress) ia[0];
                                if (!issent && !isundershared) {
                                    for (Address ax : ia) {
                                        InternetAddress iax = (InternetAddress) ax;
                                        if (iax.getAddress().equals(profile.getEmailAddress())) {
                                            iato = iax;
                                            break;
                                        }
                                    }
                                }
                                to = iato.getPersonal();
                                if (to == null)
                                    to = iato.getAddress();
                                toemail = iato.getAddress();
                            }
                            to = (to == null ? "" : StringEscapeUtils.escapeEcmaScript(MailUtils.htmlescape(to)));
                            toemail = (toemail == null ? ""
                                    : StringEscapeUtils.escapeEcmaScript(MailUtils.htmlescape(toemail)));

                            //Subject
                            String subject = xm.getSubject();
                            if (subject != null) {
                                try {
                                    subject = MailUtils.decodeQString(subject);
                                } catch (Exception exc) {

                                }
                            } else
                                subject = "";

                            /*                  if (threaded) {
                                              if (tIndent>0) {
                                                 StringBuffer sb=new StringBuffer();
                                                 for(int w=0;w<tIndent;++w) sb.append("&nbsp;");
                                                 subject=sb+subject;
                                              }
                                           }*/

                            boolean hasAttachments = mcache.hasAttachements(xm);

                            subject = StringEscapeUtils.escapeEcmaScript(MailUtils.htmlescape(subject));

                            //Unread
                            boolean unread = !xm.isSet(Flags.Flag.SEEN);
                            if (queryObj != null && unread)
                                ++funread;
                            //Priority
                            int priority = getPriority(xm);
                            //Status
                            java.util.Date today = new java.util.Date();
                            java.util.Calendar cal1 = java.util.Calendar.getInstance(locale);
                            java.util.Calendar cal2 = java.util.Calendar.getInstance(locale);
                            boolean isToday = false;
                            String gdate = "";
                            String sdate = "";
                            String xdate = "";
                            if (d != null) {
                                java.util.Date gd = sgi.threaded ? xm.getMostRecentThreadDate() : d;

                                cal1.setTime(today);
                                cal2.setTime(gd);

                                gdate = DateFormat.getDateInstance(DateFormat.MEDIUM, locale).format(gd);
                                sdate = cal2.get(java.util.Calendar.YEAR) + "/"
                                        + String.format("%02d", (cal2.get(java.util.Calendar.MONTH) + 1)) + "/"
                                        + String.format("%02d", cal2.get(java.util.Calendar.DATE));
                                //boolean isGdate=group.equals("gdate");
                                if (cal1.get(java.util.Calendar.MONTH) == cal2.get(java.util.Calendar.MONTH)
                                        && cal1.get(java.util.Calendar.YEAR) == cal2.get(java.util.Calendar.YEAR)) {
                                    int dx = cal1.get(java.util.Calendar.DAY_OF_MONTH)
                                            - cal2.get(java.util.Calendar.DAY_OF_MONTH);
                                    if (dx == 0) {
                                        isToday = true;
                                        //if (isGdate) {
                                        //   gdate=WT.lookupCoreResource(locale, CoreLocaleKey.WORD_DATE_TODAY)+"  "+gdate;
                                        //}
                                        xdate = WT.lookupCoreResource(locale, CoreLocaleKey.WORD_DATE_TODAY);
                                    } else if (dx == 1 /*&& isGdate*/) {
                                        xdate = WT.lookupCoreResource(locale, CoreLocaleKey.WORD_DATE_YESTERDAY);
                                    }
                                }
                            }

                            String status = "read";
                            if (flags != null) {
                                if (flags.contains(Flags.Flag.ANSWERED)) {
                                    if (flags.contains("$Forwarded"))
                                        status = "repfwd";
                                    else
                                        status = "replied";
                                } else if (flags.contains("$Forwarded")) {
                                    status = "forwarded";
                                } else if (flags.contains(Flags.Flag.SEEN)) {
                                    status = "read";
                                } else if (isToday) {
                                    status = "new";
                                } else {
                                    status = "unread";
                                }
                                //                    if (flags.contains(Flags.Flag.USER)) flagImage=webtopapp.getUri()+"/images/themes/"+profile.getTheme()+"/mail/flag.gif";
                            }
                            //Size
                            int msgsize = 0;
                            msgsize = (xm.getSize() * 3) / 4;// /1024 + 1;
                            //User flags
                            String cflag = "";
                            for (WebtopFlag webtopFlag : webtopFlags) {
                                String flagstring = webtopFlag.label;
                                //String tbflagstring=webtopFlag.tbLabel;
                                if (!flagstring.equals("complete")) {
                                    String oldflagstring = "flag" + flagstring;
                                    if (flags.contains(flagstring) || flags.contains(oldflagstring)
                                    /*|| (tbflagstring!=null && flags.contains(tbflagstring))*/
                                    ) {
                                        cflag = flagstring;
                                    }
                                }
                            }
                            boolean flagComplete = flags.contains("complete");
                            if (flagComplete) {
                                if (cflag.length() > 0)
                                    cflag += "-complete";
                                else
                                    cflag = "complete";
                            }

                            if (cflag.length() == 0 && flags.contains(Flags.Flag.FLAGGED))
                                cflag = "special";

                            boolean hasNote = flags.contains(sflagNote);

                            String svtags = getJSTagsArray(flags);

                            boolean autoedit = false;

                            boolean issched = false;
                            int syyyy = 0;
                            int smm = 0;
                            int sdd = 0;
                            int shhh = 0;
                            int smmm = 0;
                            int ssss = 0;
                            if (isdrafts) {
                                String h = getSingleHeaderValue(xm, "Sonicle-send-scheduled");
                                if (h != null && h.equals("true")) {
                                    java.util.Calendar scal = parseScheduleHeader(
                                            getSingleHeaderValue(xm, "Sonicle-send-date"),
                                            getSingleHeaderValue(xm, "Sonicle-send-time"));
                                    if (scal != null) {
                                        syyyy = scal.get(java.util.Calendar.YEAR);
                                        smm = scal.get(java.util.Calendar.MONTH);
                                        sdd = scal.get(java.util.Calendar.DAY_OF_MONTH);
                                        shhh = scal.get(java.util.Calendar.HOUR_OF_DAY);
                                        smmm = scal.get(java.util.Calendar.MINUTE);
                                        ssss = scal.get(java.util.Calendar.SECOND);
                                        issched = true;
                                        status = "scheduled";
                                    }
                                }

                                h = getSingleHeaderValue(xm, HEADER_SONICLE_FROM_DRAFTER);
                                if (h != null && h.equals("true")) {
                                    autoedit = true;
                                }
                            }

                            String xmfoldername = xm.getFolder().getFullName();

                            //idmessage=idmessage.replaceAll("\\\\", "\\\\");
                            //idmessage=Utils.jsEscape(idmessage);
                            if (i > 0)
                                sout += ",\n";
                            boolean archived = false;
                            if (hasDmsDocumentArchiving()) {
                                archived = xm.getHeader("X-WT-Archived") != null;
                                if (!archived) {
                                    archived = flags.contains(sflagDmsArchived);
                                }
                            }

                            String msgtext = null;
                            if (us.getShowMessagePreviewOnRow() && isToday && unread) {
                                try {
                                    msgtext = MailUtils.peekText(xm);
                                    if (msgtext != null) {
                                        msgtext = msgtext.trim();
                                        if (msgtext.length() > 100)
                                            msgtext = msgtext.substring(0, 100);
                                    }
                                } catch (MessagingException | IOException ex1) {
                                    msgtext = ex1.getMessage();
                                }
                            }

                            String pecstatus = null;
                            if (messagesInfo.isPEC()) {
                                String hdrs[] = xm.getHeader(HDR_PEC_TRASPORTO);
                                if (hdrs != null && hdrs.length > 0
                                        && (hdrs[0].equals("errore") || hdrs[0].equals("posta-certificata")))
                                    pecstatus = hdrs[0];
                                else {
                                    hdrs = xm.getHeader(HDR_PEC_RICEVUTA);
                                    if (hdrs != null && hdrs.length > 0)
                                        pecstatus = hdrs[0];
                                }
                            }

                            sout += "{idmessage:'" + nuid + "'," + "priority:" + priority + "," + "status:'"
                                    + status + "'," + "to:'" + to + "'," + "toemail:'" + toemail + "'," + "from:'"
                                    + from + "'," + "fromemail:'" + fromemail + "'," + "subject:'" + subject + "',"
                                    + (msgtext != null
                                            ? "msgtext: '" + StringEscapeUtils.escapeEcmaScript(msgtext) + "',"
                                            : "")
                                    + (sgi.threaded ? "threadId: " + tId + "," : "")
                                    + (sgi.threaded ? "threadIndent:" + tIndent + "," : "") + "date: new Date("
                                    + yyyy + "," + mm + "," + dd + "," + hhh + "," + mmm + "," + sss + "),"
                                    + "gdate: '" + gdate + "'," + "sdate: '" + sdate + "'," + "xdate: '" + xdate
                                    + "'," + "unread: " + unread + "," + "size:" + msgsize + ","
                                    + (svtags != null ? "tags: " + svtags + "," : "")
                                    + (pecstatus != null ? "pecstatus: '" + pecstatus + "'," : "") + "flag:'"
                                    + cflag + "'" + (hasNote ? ",note:true" : "") + (archived ? ",arch:true" : "")
                                    + (isToday ? ",istoday:true" : "") + (hasAttachments ? ",atts:true" : "")
                                    + (issched
                                            ? ",scheddate: new Date(" + syyyy + "," + smm + "," + sdd + "," + shhh
                                                    + "," + smmm + "," + ssss + ")"
                                            : "")
                                    + (sgi.threaded && tIndent == 0 ? ",threadOpen: " + mcache.isThreadOpen(nuid)
                                            : "")
                                    + (sgi.threaded && tIndent == 0 ? ",threadHasChildren: " + tChildren : "")
                                    + (sgi.threaded && tIndent == 0 ? ",threadUnseenChildren: " + tUnseenChildren
                                            : "")
                                    + (sgi.threaded && xm.hasThreads() && !xm.isMostRecentInThread() ? ",fmtd: true"
                                            : "")
                                    + (sgi.threaded && !xmfoldername.equals(folder.getFullName()) ? ",fromfolder: '"
                                            + StringEscapeUtils.escapeEcmaScript(xmfoldername) + "'" : "")
                                    + "}";

                            if (autoedit) {
                                autoeditList.add(nuid);
                            }

                            //                sout+="{messageid:'"+m.getMessageID()+"',from:'"+from+"',subject:'"+subject+"',date: new Date("+yyyy+","+mm+","+dd+"),unread: "+unread+"},\n";
                        }

                        if (openedsent)
                            fsent.close(false);
                    }
                    /*                if (ppattern==null && !isSpecialFolder(mcache.getFolderName())) {
                     //if (max<msgs.length) mcache.fetch(msgs,FolderCache.flagsFP,max,msgs.length);
                     for(int i=max;i<msgs.length;++i) {
                     try {
                     if (!msgs[i].isSet(Flags.Flag.SEEN)) funread++;
                     } catch(Exception exc) {
                        
                     }
                     }
                     } else {
                     funread=mcache.getUnreadMessagesCount();
                     }*/
                    if (mcache.isScanForcedOrEnabled()) {
                        //Send message only if first page
                        if (start == 0)
                            mcache.refreshUnreads();
                        funread = mcache.getUnreadMessagesCount();
                    } else
                        funread = 0;

                    long qlimit = -1;
                    long qusage = -1;
                    try {
                        Quota quotas[] = account.getQuota("INBOX");
                        if (quotas != null)
                            for (Quota q : quotas) {
                                if ((q.quotaRoot.equals("INBOX") || q.quotaRoot.equals("Quota"))
                                        && q.resources != null) {
                                    for (Quota.Resource r : q.resources) {
                                        if (r.name.equals("STORAGE")) {
                                            qlimit = r.limit;
                                            qusage = r.usage;
                                        }
                                    }
                                }
                            }
                    } catch (MessagingException exc) {
                        logger.debug("Error on QUOTA", exc);
                    }

                    sout += "\n],\n";
                    sout += "total: " + (total - expunged) + ",\n";
                    if (qlimit >= 0 && qusage >= 0)
                        sout += "quotaLimit: " + qlimit + ", quotaUsage: " + qusage + ",\n";
                    if (messagesInfo.isPEC())
                        sout += "isPEC: true,\n";
                    sout += "realTotal: " + (xmsgs.length - expunged) + ",\n";
                    sout += "expunged: " + (expunged) + ",\n";
                } else {
                    sout += "messages: [],\n" + "total: 0,\n" + "realTotal: 0,\n" + "expunged:0,\n";
                }
                sout += "metaData: {\n" + "  root: 'messages', total: 'total', idProperty: 'idmessage',\n"
                        + "  fields: ['idmessage','priority','status','to','from','subject','date','gdate','unread','size','flag','note','arch','istoday','atts','scheddate','fmtd','fromfolder'],\n"
                        + "  sortInfo: { field: '" + psortfield + "', direction: '" + psortdir + "' },\n"
                        + "  threaded: " + sgi.threaded + ",\n" + "  groupField: '"
                        + (sgi.threaded ? "threadId" : group) + "',\n";

                /*            ColumnVisibilitySetting cvs = us.getColumnVisibilitySetting(pfoldername);
                            ColumnsOrderSetting cos = us.getColumnsOrderSetting();
                            // Apply grid defaults
                            //ColumnVisibilitySetting.applyDefaults(mcache.isSent(), cvs);
                            ColumnVisibilitySetting.applyDefaults(issent||isundersent, cvs);
                    
                            if (autoeditList.size()>0) {
                               sout+="autoedit: [";
                               for(long muid: autoeditList) {
                                  sout+=muid+",";
                               }
                               if(StringUtils.right(sout, 1).equals(",")) sout = StringUtils.left(sout, sout.length()-1);
                               sout+="],\n";
                            }
                                
                            // Fills columnsInfo object for client rendering
                            sout += "colsInfo2: [";
                            for (String dataIndex : cvs.keySet()) {
                               sout += "{dataIndex:'" + dataIndex + "',hidden:" + String.valueOf(!cvs.get(dataIndex)) + ",index:"+cos.indexOf(dataIndex)+"},";
                            }
                            if (StringUtils.right(sout, 1).equals(",")) {
                               sout = StringUtils.left(sout, sout.length() - 1);
                            }
                            sout += "]\n";*/

                sout += "},\n";
                sout += "threaded: " + (sgi.threaded ? "1" : "0") + ",\n";
                sout += "unread: " + funread + ", issent: " + issent + ", millis: " + messagesInfo.millis + " }\n";

            } else {
                sout += "total:0,\nstart:0,\nlimit:0,\nmessages: [\n";
                sout += "\n], unread: 0, issent: false }\n";
            }
            out.println(sout);
        } catch (Exception exc) {
            new JsonResult(exc).printTo(out);
            Service.logger.error("Exception", exc);
        }
    }

    public void processGetMessagePage(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String pfoldername = request.getParameter("folder");
        String puid = request.getParameter("uid");
        String prowsperpage = request.getParameter("rowsperpage");
        //String psearchfield = request.getParameter("searchfield");
        //String ppattern = request.getParameter("pattern");
        //String pquickfilter=request.getParameter("quickfilter");
        //String prefresh = request.getParameter("refresh");
        //String ptimestamp = request.getParameter("timestamp");
        //if (psearchfield != null && psearchfield.trim().length() == 0) {
        //   psearchfield = null;
        //}
        //if (ppattern != null && ppattern.trim().length() == 0) {
        //   ppattern = null;
        //}
        //if (pquickfilter!=null && pquickfilter.trim().length()==0) pquickfilter=null;
        //boolean refresh = (prefresh != null && prefresh.equals("true"));
        //long timestamp=Long.parseLong(ptimestamp);
        boolean refresh = true;
        long uid = Long.parseLong(puid);
        long rowsperpage = Long.parseLong(prowsperpage);

        String group = us.getMessageListGroup(pfoldername);
        if (group == null) {
            group = "";
        }

        String psortfield = "date";
        String psortdir = "DESC";
        try {
            boolean nogroup = group.equals("");
            if (nogroup) {
                String s = us.getMessageListSort(pfoldername);
                int ix = s.indexOf("|");
                psortfield = s.substring(0, ix);
                psortdir = s.substring(ix + 1);
            } else {
                psortfield = "date";
                psortdir = "DESC";
            }
        } catch (Exception exc) {
            logger.error("Exception", exc);
        }

        SortGroupInfo sgi = getSortGroupInfo(psortfield, psortdir, group);

        Folder folder = null;
        boolean connected = false;
        try {
            connected = account.checkStoreConnected();
            if (!connected)
                throw new Exception("Mail account authentication error");

            if (pfoldername == null) {
                folder = account.getDefaultFolder();
            } else {
                folder = account.getFolder(pfoldername);
            }

            String key = folder.getFullName();
            FolderCache mcache = account.getFolderCache(key);
            if (mcache.toBeRefreshed())
                refresh = true;
            //         if (ppattern != null && psearchfield != null) {
            //            key += "|" + ppattern + "|" + psearchfield;
            //         }
            if (psortfield != null && psortdir != null) {
                key += "|" + psortdir + "|" + psortfield;
            }
            //         if (pquickfilter !=null) {
            //            key +="|" + pquickfilter;
            //         }

            MessagesInfo messagesInfo = listMessages(mcache, key, refresh, sgi, 0, null, false);
            Message xmsgs[] = messagesInfo.messages;
            if (xmsgs != null) {
                boolean found = false;
                int start = 0;
                int startx = 0;
                long tId = 0;
                for (int i = 0, ni = 0; i < xmsgs.length; ++ni, ++i) {
                    int ix = start + i;
                    int nx = start + ni;
                    if (nx >= xmsgs.length)
                        break;

                    SonicleIMAPMessage xm = (SonicleIMAPMessage) xmsgs[nx];
                    if (xm.isExpunged()) {
                        --i;
                        continue;
                    }

                    long nuid = mcache.getUID(xm);
                    int tIndent = xm.getThreadIndent();

                    if (nuid == uid) {
                        found = true;
                        if (tIndent == 0)
                            tId = 0;
                        //else tId contains the last thread root id
                        break;
                    }

                    if (tIndent == 0)
                        tId = nuid;
                    else if (sgi.threaded) {
                        if (!mcache.isThreadOpen(tId)) {
                            --i;
                            continue;
                        }
                    }

                    ++startx;
                }
                if (found) {
                    JsonResult jsr = new JsonResult();
                    jsr.set("page", ((int) (startx / rowsperpage) + 1));
                    jsr.set("row", startx);
                    if (tId > 0)
                        jsr.set("threadid", tId);
                    jsr.printTo(out);
                } else
                    new JsonResult(false, "Message not found");
            } else
                new JsonResult(false, "No messages");
        } catch (Exception exc) {
            new JsonResult(exc).printTo(out);
        }
    }

    private String getSingleHeaderValue(Message m, String headerName) throws MessagingException {
        String s[] = m.getHeader(headerName);
        String sv = null;
        if (s != null && s.length > 0) {
            sv = s[0];
        }
        return sv;
    }

    private java.util.Calendar parseScheduleHeader(String senddate, String sendtime) {
        String sdp[] = senddate.split("/");
        String sdt[] = sendtime.split(":");
        if (sdp.length < 3 || sdt.length < 2)
            return null;

        String sschedday = sdp[0];
        String sschedmonth = sdp[1];
        String sschedyear = sdp[2];
        String sschedhour = sdt[0];
        String sschedmins = sdt[1];
        int schedday = Integer.parseInt(sschedday);
        int schedmonth = Integer.parseInt(sschedmonth);
        int schedyear = Integer.parseInt(sschedyear);
        int schedhour = Integer.parseInt(sschedhour);
        int schedmins = Integer.parseInt(sschedmins);
        java.util.Calendar cal = java.util.Calendar.getInstance();
        cal.set(java.util.Calendar.YEAR, schedyear);
        cal.set(java.util.Calendar.MONTH, schedmonth - 1);
        cal.set(java.util.Calendar.DATE, schedday);
        cal.set(java.util.Calendar.HOUR_OF_DAY, schedhour);
        cal.set(java.util.Calendar.MINUTE, schedmins);
        return cal;
    }

    class MessageListThread implements Runnable {

        FolderCache fc;
        String pattern;
        String searchfield;
        int sortby;
        boolean ascending;
        int sort_group;
        boolean groupascending;
        boolean refresh;
        long millis;
        SearchTerm searchTerm;
        boolean hasAttachment;

        boolean isPec = false;
        HashMap<String, Message> hpecsent = null;

        boolean threaded = false;

        Message msgs[] = null;
        boolean started = false;
        boolean finished = false;
        final Object lock = new Object();
        long lastRequest = 0;

        MessageListThread(FolderCache fc, int sortby, boolean ascending, boolean refresh, int sort_group,
                boolean groupascending, boolean threaded, SearchTerm searchTerm, boolean hasAttachment) {
            this.fc = fc;
            this.sortby = sortby;
            this.ascending = ascending;
            this.refresh = refresh;
            this.sort_group = sort_group;
            this.groupascending = groupascending;
            this.threaded = threaded;
            this.searchTerm = searchTerm;
            this.hasAttachment = hasAttachment;
        }

        public void run() {
            started = true;
            finished = false;
            synchronized (lock) {
                try {
                    this.millis = System.currentTimeMillis();
                    msgs = fc.getMessages(sortby, ascending, refresh, sort_group, groupascending, threaded,
                            searchTerm, hasAttachment);

                    /*
                    UserProfileId profileId=getEnv().getProfileId();
                    String domainId=profileId.getDomainId();
                    if (fc.isUnderSharedFolder()) {
                       SharedPrincipal sp=fc.getSharedInboxPrincipal();
                       if (sp!=null) profileId=new UserProfileId(domainId, sp.getUserId());
                       else profileId=null;
                    }
                    if (profileId!=null)
                       isPec=RunContext.hasRole(profileId, WT.getGroupUidForPecAccounts(profileId.getDomainId()));
                    */
                    isPec = fc.isPEC();

                    /*               if (isPec) {
                                      Message pmsgs[]=fc.searchMessagesByXHeader(HDR_PEC_RICEVUTA,HDR_PEC_RICEVUTA_VALUE_AVVENUTA_CONSEGNA);
                                      fc.fetch(pmsgs, pecFP);
                                      hpecsent=new HashMap<>();
                                      for(Message pmsg: pmsgs) {
                                         String hdrs[]=pmsg.getHeader(HDR_PEC_RIFERIMENTO_MESSAGE_ID);
                                         if (hdrs!=null && hdrs.length>0)
                    hpecsent.put(hdrs[0], pmsg);
                                      }
                                      System.out.println("loaded PEC hash");
                                   }*/
                } catch (Exception exc) {
                    Service.logger.error("Exception", exc);
                } finally {
                    finished = true;
                    lock.notifyAll();
                }
            }
        }

        /*      public boolean checkSkipPEC(Message xm) throws MessagingException {
                 if (isPec) {
        String hdrs[]=xm.getHeader(HDR_PEC_RICEVUTA);
        if (hdrs!=null && hdrs.length>0 && hdrs[0].equals(HDR_PEC_RICEVUTA_VALUE_ACCETTAZIONE)) {                     
           hdrs=xm.getHeader(HDR_PEC_RIFERIMENTO_MESSAGE_ID);
           if (hdrs!=null && hdrs.length>0) {
              return hpecsent.containsKey(hdrs[0]);
           }
        }
                 }
                 return false;
              }*/

    }

    private String getJSTagsArray(Flags flags) {
        ArrayList<Tag> tags = null;
        String svtags = null;
        if (flags != null) {
            for (Tag tag : atags) {
                if (flags.contains(tag.getTagId())) {
                    if (tags == null)
                        tags = new ArrayList<>();
                    tags.add(tag);
                }
            }
            if (tags != null) {
                for (Tag tag : tags) {
                    if (svtags == null)
                        svtags = "[ ";
                    else
                        svtags += ",";
                    svtags += "'" + StringEscapeUtils.escapeEcmaScript(tag.getTagId()) + "'";
                }
                if (svtags != null)
                    svtags += " ]";
            }
        }
        return svtags;
    }

    DateFormat df = null;

    public void processGetMessage(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String pfoldername = request.getParameter("folder");
        String puidmessage = request.getParameter("idmessage");
        String pidattach = request.getParameter("idattach");
        String providername = request.getParameter("provider");
        String providerid = request.getParameter("providerid");
        String nopec = request.getParameter("nopec");
        int idattach = 0;
        boolean isEditor = request.getParameter("editor") != null;
        boolean setSeen = ServletUtils.getBooleanParameter(request, "setseen", true);

        if (df == null) {
            df = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM,
                    environment.getProfile().getLocale());
        }
        try {
            FolderCache mcache = null;
            Message m = null;
            IMAPMessage im = null;
            int recs = 0;
            long msguid = -1;
            String vheader[] = null;
            boolean wasseen = false;
            boolean isPECView = false;
            String sout = "{\nmessage: [\n";
            if (providername == null) {
                account.checkStoreConnected();
                mcache = account.getFolderCache(pfoldername);
                msguid = Long.parseLong(puidmessage);
                m = mcache.getMessage(msguid);
                im = (IMAPMessage) m;
                im.setPeek(us.isManualSeen());
                if (m.isExpunged())
                    throw new MessagingException("Message " + puidmessage + " expunged");
                vheader = m.getHeader("Disposition-Notification-To");
                wasseen = m.isSet(Flags.Flag.SEEN);
                if (pidattach != null) {

                    HTMLMailData mailData = mcache.getMailData((MimeMessage) m);
                    Part part = mailData.getAttachmentPart(Integer.parseInt(pidattach));
                    m = (Message) part.getContent();
                    idattach = Integer.parseInt(pidattach) + 1;
                } else if (nopec == null && mcache.isPEC()) {
                    String hdrs[] = m.getHeader(HDR_PEC_TRASPORTO);
                    if (hdrs != null && hdrs.length > 0 && hdrs[0].equals("posta-certificata")) {
                        HTMLMailData mailData = mcache.getMailData((MimeMessage) m);
                        int parts = mailData.getAttachmentPartCount();
                        for (int i = 0; i < parts; ++i) {
                            Part p = mailData.getAttachmentPart(i);
                            if (p.isMimeType("message/rfc822")) {
                                m = (Message) p.getContent();
                                idattach = i + 1;
                                isPECView = true;
                                break;
                            }
                        }
                    }
                }
            } else {
                // TODO: provider get message!!!!
                /*                WebTopService provider=wts.getServiceByName(providername);
                             MessageContentProvider mcp=provider.getMessageContentProvider(providerid);
                             m=new MimeMessage(session,mcp.getSource());
                             mcache=fcProvided;
                             mcache.addProvidedMessage(providername, providerid, m);*/
            }
            String messageid = getMessageID(m);
            String subject = m.getSubject();
            if (subject == null) {
                subject = "";
            } else {
                try {
                    subject = MailUtils.decodeQString(subject);
                } catch (Exception exc) {

                }
            }
            java.util.Date d = m.getSentDate();
            if (d == null) {
                d = m.getReceivedDate();
            }
            if (d == null) {
                d = new java.util.Date(0);
            }
            String date = df.format(d).replaceAll("\\.", ":");
            String fromName = "";
            String fromEmail = "";
            Address as[] = m.getFrom();
            InternetAddress iafrom = null;
            if (as != null && as.length > 0) {
                iafrom = (InternetAddress) as[0];
                fromName = iafrom.getPersonal();
                fromEmail = adjustEmail(iafrom.getAddress());
                if (fromName == null) {
                    fromName = fromEmail;
                }
            }
            sout += "{iddata:'from',value1:'" + StringEscapeUtils.escapeEcmaScript(MailUtils.htmlescape(fromName))
                    + "',value2:'" + StringEscapeUtils.escapeEcmaScript(fromEmail) + "',value3:0},\n";
            recs += 2;
            Address tos[] = m.getRecipients(RecipientType.TO);
            if (tos != null) {
                for (Address to : tos) {
                    InternetAddress ia = (InternetAddress) to;
                    String toName = ia.getPersonal();
                    String toEmail = adjustEmail(ia.getAddress());
                    if (toName == null) {
                        toName = toEmail;
                    }
                    sout += "{iddata:'to',value1:'"
                            + StringEscapeUtils.escapeEcmaScript(MailUtils.htmlescape(toName)) + "',value2:'"
                            + StringEscapeUtils.escapeEcmaScript(toEmail) + "',value3:0},\n";
                    ++recs;
                }
            }
            Address ccs[] = m.getRecipients(RecipientType.CC);
            if (ccs != null) {
                for (Address cc : ccs) {
                    InternetAddress ia = (InternetAddress) cc;
                    String ccName = ia.getPersonal();
                    String ccEmail = adjustEmail(ia.getAddress());
                    if (ccName == null) {
                        ccName = ccEmail;
                    }
                    sout += "{iddata:'cc',value1:'" + StringEscapeUtils.escapeEcmaScript(ccName) + "',value2:'"
                            + StringEscapeUtils.escapeEcmaScript(ccEmail) + "',value3:0},\n";
                    ++recs;
                }
            }
            Address bccs[] = m.getRecipients(RecipientType.BCC);
            if (bccs != null)
                for (Address bcc : bccs) {
                    InternetAddress ia = (InternetAddress) bcc;
                    String bccName = ia.getPersonal();
                    String bccEmail = adjustEmail(ia.getAddress());
                    if (bccName == null) {
                        bccName = bccEmail;
                    }
                    sout += "{iddata:'bcc',value1:'" + StringEscapeUtils.escapeEcmaScript(bccName) + "',value2:'"
                            + StringEscapeUtils.escapeEcmaScript(bccEmail) + "',value3:0},\n";
                    ++recs;
                }
            ArrayList<String> htmlparts = null;
            boolean balanceTags = isPreviewBalanceTags(iafrom);
            if (providername == null) {
                htmlparts = mcache.getHTMLParts((MimeMessage) m, msguid, false, balanceTags);
            } else {
                htmlparts = mcache.getHTMLParts((MimeMessage) m, providername, providerid, balanceTags);
            }
            HTMLMailData mailData = mcache.getMailData((MimeMessage) m);
            ICalendarRequest ir = mailData.getICalRequest();
            if (ir != null) {
                if (htmlparts.size() > 0)
                    sout += "{iddata:'html',value1:'" + StringEscapeUtils.escapeEcmaScript(htmlparts.get(0))
                            + "',value2:'',value3:0},\n";
            } else {
                for (String html : htmlparts) {
                    //sout += "{iddata:'html',value1:'" + OldUtils.jsEscape(html) + "',value2:'',value3:0},\n";
                    sout += "{iddata:'html',value1:'" + StringEscapeUtils.escapeEcmaScript(html)
                            + "',value2:'',value3:0},\n";
                    ++recs;
                }
            }

            /*if (!wasseen) {
               //if (us.isManualSeen()) {
               if (!setSeen) {
                  m.setFlag(Flags.Flag.SEEN, false);
               } else {
                  //if no html part, flag seen is not set
                  if (htmlparts.size()==0) m.setFlag(Flags.Flag.SEEN, true);
               }
            }*/
            if (!us.isManualSeen()) {
                if (htmlparts.size() == 0)
                    m.setFlag(Flags.Flag.SEEN, true);
            } else {
                if (setSeen)
                    m.setFlag(Flags.Flag.SEEN, true);
            }

            int acount = mailData.getAttachmentPartCount();
            for (int i = 0; i < acount; ++i) {
                Part p = mailData.getAttachmentPart(i);
                String ctype = p.getContentType();
                Service.logger.debug("attachment " + i + " is " + ctype);
                int ix = ctype.indexOf(';');
                if (ix > 0) {
                    ctype = ctype.substring(0, ix);
                }
                String cidnames[] = p.getHeader("Content-ID");
                String cidname = null;
                if (cidnames != null && cidnames.length > 0)
                    cidname = mcache.normalizeCidFileName(cidnames[0]);
                boolean isInlineable = isInlineableMime(ctype);
                boolean inline = ((p.getHeader("Content-Location") != null) || (cidname != null)) && isInlineable;
                if (inline && cidname != null)
                    inline = mailData.isReferencedCid(cidname);
                if (p.getDisposition() != null && p.getDisposition().equalsIgnoreCase(Part.INLINE) && inline) {
                    continue;
                }

                String imgname = null;
                boolean isCalendar = ctype.equalsIgnoreCase("text/calendar")
                        || ctype.equalsIgnoreCase("text/icalendar");
                if (isCalendar) {
                    imgname = "resources/" + getManifest().getId() + "/laf/" + cus.getLookAndFeel()
                            + "/ical_16.png";
                }

                String pname = getPartName(p);
                try {
                    pname = MailUtils.decodeQString(pname);
                } catch (Exception exc) {
                }
                if (pname == null) {
                    ix = ctype.indexOf("/");
                    String fname = ctype;
                    if (ix > 0) {
                        fname = ctype.substring(ix + 1);
                    }
                    //String ext = WT.getMediaTypeExtension(ctype);
                    //if (ext == null) {
                    pname = fname;
                    //} else {
                    //   pname = fname + "." + ext;
                    //}
                    if (isCalendar)
                        pname += ".ics";
                } else {
                    if (isCalendar && !StringUtils.endsWithIgnoreCase(pname, ".ics"))
                        pname += ".ics";
                }
                int size = p.getSize();
                int lines = (size / 76);
                int rsize = size - (lines * 2);//(p.getSize()/4)*3;
                String iddata = ctype.equalsIgnoreCase("message/rfc822") ? "eml"
                        : (inline ? "inlineattach" : "attach");
                boolean editable = isFileEditableInDocEditor(pname);

                sout += "{iddata:'" + iddata + "',value1:'" + (i + idattach) + "',value2:'"
                        + StringEscapeUtils.escapeEcmaScript(MailUtils.htmlescape(pname)) + "',value3:" + rsize
                        + ",value4:"
                        + (imgname == null ? "null" : "'" + StringEscapeUtils.escapeEcmaScript(imgname) + "'")
                        + ", editable: " + editable + " },\n";
            }
            if (!mcache.isDrafts() && !mcache.isSent() && !mcache.isSpam() && !mcache.isTrash()
                    && !mcache.isArchive()) {
                if (vheader != null && vheader[0] != null && !wasseen) {
                    sout += "{iddata:'receipt',value1:'" + us.getReadReceiptConfirmation() + "',value2:'"
                            + StringEscapeUtils.escapeEcmaScript(vheader[0]) + "',value3:0},\n";
                }
            }

            String h = getSingleHeaderValue(m, "Sonicle-send-scheduled");
            if (h != null && h.equals("true")) {
                java.util.Calendar scal = parseScheduleHeader(getSingleHeaderValue(m, "Sonicle-send-date"),
                        getSingleHeaderValue(m, "Sonicle-send-time"));
                if (scal != null) {
                    java.util.Date sd = scal.getTime();
                    String sdate = df.format(sd).replaceAll("\\.", ":");
                    sout += "{iddata:'scheddate',value1:'" + StringEscapeUtils.escapeEcmaScript(sdate)
                            + "',value2:'',value3:0},\n";
                }
            }

            if (ir != null) {

                /*
                ICalendarManager calMgr = (ICalendarManager)WT.getServiceManager("com.sonicle.webtop.calendar",environment.getProfileId());
                if (calMgr != null) {
                   if (ir.getMethod().equals("REPLY")) {
                      calMgr.updateEventFromICalReply(ir.getCalendar());
                      //TODO: gestire lato client una notifica di avvenuto aggiornamento
                   } else {
                      Event evt = calMgr..getEvent(GetEventScope.PERSONAL_AND_INCOMING, false, ir.getUID())
                      if (evt != null) {
                 UserProfileId pid = getEnv().getProfileId();
                 UserProfile.Data ud = WT.getUserData(pid);
                 boolean iAmOrganizer = StringUtils.equalsIgnoreCase(evt.getOrganizerAddress(), ud.getEmailAddress());
                 boolean iAmOwner = pid.equals(calMgr.getCalendarOwner(evt.getCalendarId()));
                     
                 if (!iAmOrganizer && !iAmOwner) {
                    //TODO: gestire lato client l'aggiornamento: Accetta/Rifiuta, Aggiorna e20 dopo update/request
                 }
                      }
                   }
                }
                */

                ICalendarManager cm = (ICalendarManager) WT.getServiceManager("com.sonicle.webtop.calendar", true,
                        environment.getProfileId());
                if (cm != null) {
                    int eid = -1;
                    //Event ev=cm.getEventByScope(EventScope.PERSONAL_AND_INCOMING, ir.getUID());
                    Event ev = null;
                    if (ir.getMethod().equals("REPLY")) {
                        // Previous impl. forced (forceOriginal == true)
                        ev = cm.getEvent(GetEventScope.PERSONAL_AND_INCOMING, ir.getUID());
                    } else {
                        ev = cm.getEvent(GetEventScope.PERSONAL_AND_INCOMING, ir.getUID());
                    }

                    UserProfileId pid = getEnv().getProfileId();
                    UserProfile.Data ud = WT.getUserData(pid);

                    if (ev != null) {
                        InternetAddress organizer = InternetAddressUtils.toInternetAddress(ev.getOrganizer());
                        boolean iAmOwner = pid.equals(cm.getCalendarOwner(ev.getCalendarId()));
                        boolean iAmOrganizer = (organizer != null)
                                && StringUtils.equalsIgnoreCase(organizer.getAddress(), ud.getEmailAddress());

                        //TODO: in reply controllo se mail combacia con quella dell'attendee che risponde...
                        //TODO: rimuovere controllo su data? dovrebbe sempre aggiornare?

                        if (iAmOwner || iAmOrganizer) {
                            eid = 0;
                            //TODO: troviamo un modo per capire se la risposta si riverisce all'ultima versione dell'evento? Nuovo campo timestamp?
                            /*
                            DateTime dtEvt = ev.getRevisionTimestamp().withMillisOfSecond(0).withZone(DateTimeZone.UTC);
                            DateTime dtICal = ICal4jUtils.fromICal4jDate(ir.getLastModified(), ICal4jUtils.getTimeZone(DateTimeZone.UTC));
                            if (dtICal.isAfter(dtEvt)) {
                               eid = 0;
                            } else {
                               eid = ev.getEventId();
                            }
                            */
                        }
                    }
                    sout += "{iddata:'ical',value1:'" + ir.getMethod() + "',value2:'" + ir.getUID() + "',value3:'"
                            + eid + "'},\n";
                }
            }

            sout += "{iddata:'date',value1:'" + StringEscapeUtils.escapeEcmaScript(date)
                    + "',value2:'',value3:0},\n";
            sout += "{iddata:'subject',value1:'" + StringEscapeUtils.escapeEcmaScript(MailUtils.htmlescape(subject))
                    + "',value2:'',value3:0},\n";
            sout += "{iddata:'messageid',value1:'" + StringEscapeUtils.escapeEcmaScript(messageid)
                    + "',value2:'',value3:0}\n";

            if (providername == null && !mcache.isSpecial()) {
                mcache.refreshUnreads();
            }
            long millis = System.currentTimeMillis();
            sout += "\n],\n";

            String svtags = getJSTagsArray(m.getFlags());
            if (svtags != null)
                sout += "tags: " + svtags + ",\n";

            if (isPECView) {
                sout += "pec: true,\n";
            }

            sout += "total:" + recs + ",\nmillis:" + millis + "\n}\n";
            out.println(sout);

            if (im != null)
                im.setPeek(false);

            //            if (!wasopen) folder.close(false);
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
        }
    }

    public void processGetContactFromVCard(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        try {
            String folder = ServletUtils.getStringParameter(request, "folder", true);
            int messageId = ServletUtils.getIntParameter(request, "messageId", true);
            int attachId = ServletUtils.getIntParameter(request, "attachId", true);
            String uploadTag = ServletUtils.getStringParameter(request, "uploadTag", true);

            MailAccount account = getAccount(request);
            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(folder);
            Message m = mcache.getMessage(messageId);
            HTMLMailData mailData = mcache.getMailData((MimeMessage) m);
            Part part = mailData.getAttachmentPart(attachId);
            String filename = MailUtils.getPartFilename(part);

            InputStream is = part.getInputStream();
            try {
                List<ContactInput> results = new VCardInput().fromVCardFile(is, null);
                ContactInput ci = results.get(0);

                JsContactData js = new JsContactData(ci.contact);

                if (ci.contact.hasPicture()) {
                    ContactPictureWithBytes picture = (ContactPictureWithBytes) ci.contact.getPicture();
                    WebTopSession.UploadedFile upl = null;
                    ByteArrayInputStream bais = null;
                    try {
                        bais = new ByteArrayInputStream(picture.getBytes());
                        upl = addAsUploadedFile("com.sonicle.webtop.contacts", uploadTag,
                                StringUtils.defaultIfBlank(filename, "idAttach"), "text/vcard", bais);
                    } finally {
                        IOUtils.closeQuietly(bais);
                    }
                    if (upl != null)
                        js.picture = upl.getUploadId();
                }
                new JsonResult(js).printTo(out);

            } finally {
                IOUtils.closeQuietly(is);
            }

        } catch (Exception ex) {
            Service.logger.error("Exception", ex);
            new JsonResult(ex).printTo(out);
        }
    }

    public void processGetMessageNote(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String pfoldername = request.getParameter("folder");
        String puidmessage = request.getParameter("idmessage");
        UserProfile profile = environment.getProfile();
        Connection con = null;
        boolean result = false;
        String text = "";
        try {
            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(pfoldername);
            long msguid = Long.parseLong(puidmessage);
            String id = getMessageID(mcache.getMessage(msguid));
            con = getConnection();
            ONote onote = NoteDAO.getInstance().selectById(con, profile.getDomainId(), id);
            if (onote != null) {
                text = onote.getText();
            }
            result = true;
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            text = exc.getMessage();
        } finally {
            DbUtils.closeQuietly(con);
        }
        new JsonResult(result, text).printTo(out);
    }

    public void processSaveMessageNote(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String pfoldername = request.getParameter("folder");
        String puidmessage = request.getParameter("idmessage");
        String text = request.getParameter("text").trim();
        UserProfile profile = environment.getProfile();
        Connection con = null;
        boolean result = false;
        String message = "";
        try {
            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(pfoldername);
            long msguid = Long.parseLong(puidmessage);
            Message msg = mcache.getMessage(msguid);
            String id = getMessageID(msg);
            con = getConnection();
            NoteDAO.getInstance().deleteById(con, profile.getDomainId(), id);
            if (text.length() > 0) {
                ONote onote = new ONote(profile.getDomainId(), id, text);
                NoteDAO.getInstance().insert(con, onote);
                msg.setFlags(flagNote, true);
            } else {
                msg.setFlags(flagNote, false);
            }
            result = true;
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            message = exc.getMessage();
        } finally {
            DbUtils.closeQuietly(con);
        }
        new JsonResult(result, message).printTo(out);
    }

    protected String adjustEmail(String email) {
        if (email != null) {
            email = email.trim();
            if (email.startsWith("'")) {
                email = email.substring(1);
            }
            if (email.endsWith("'")) {
                email = email.substring(0, email.length() - 1);
            }
            email = email.trim();
        }
        return email;
    }

    class ContactElement {

        String email;
        String source;

        ContactElement(String email, String source) {
            this.email = email;
            this.source = source;
        }

        public boolean equals(Object o) {
            ContactElement contact = (ContactElement) o;
            return email.equals(contact.email);
        }
    }

    public void processGetAttachment(HttpServletRequest request, HttpServletResponse response) {
        MailAccount account = getAccount(request);
        String pfoldername = request.getParameter("folder");
        String puidmessage = request.getParameter("idmessage");
        String pidattach = request.getParameter("idattach");
        String providername = request.getParameter("provider");
        String providerid = request.getParameter("providerid");
        String pcid = request.getParameter("cid");
        String purl = request.getParameter("url");
        String punknown = request.getParameter("unknown");
        String psaveas = request.getParameter("saveas");

        try {
            account.checkStoreConnected();
            FolderCache mcache = null;
            Message m = null;
            if (providername == null) {
                mcache = account.getFolderCache(pfoldername);
                long newmsguid = Long.parseLong(puidmessage);
                m = mcache.getMessage(newmsguid);
            } else {
                mcache = fcProvided;
                m = mcache.getProvidedMessage(providername, providerid);
            }
            IMAPMessage im = (IMAPMessage) m;
            im.setPeek(us.isManualSeen());
            HTMLMailData mailData = mcache.getMailData((MimeMessage) m);
            Part part = null;
            if (pcid != null) {
                part = mailData.getCidPart(pcid);
            } else if (purl != null) {
                part = mailData.getUrlPart(purl);
            } else if (pidattach != null) {
                part = mailData.getAttachmentPart(Integer.parseInt(pidattach));
            } else if (punknown != null) {
                part = mailData.getUnknownPart(Integer.parseInt(punknown));
            }

            //boolean wasseen = m.isSet(Flags.Flag.SEEN);
            if (part != null) {
                String ctype = "binary/octet-stream";
                if (psaveas == null) {
                    ctype = part.getContentType();
                    int ix = ctype.indexOf(";");
                    if (ix > 0)
                        ctype = ctype.substring(0, ix);
                }
                String name = part.getFileName();
                if (name == null)
                    name = "";
                try {
                    name = MailUtils.decodeQString(name);
                } catch (Exception exc) {
                }
                name = name.trim();
                if (psaveas == null) {
                    int ix = name.lastIndexOf(".");
                    if (ix > 0) {
                        //String ext=name.substring(ix+1);
                        String xctype = ServletHelper.guessMediaType(name);
                        if (xctype != null)
                            ctype = xctype;
                    }
                }
                ServletUtils.setFileStreamHeaders(response, ctype, DispositionType.INLINE, name);
                if (providername == null) {
                    Folder folder = mailData.getFolder();
                    if (!folder.isOpen())
                        folder.open(Folder.READ_ONLY);
                }
                InputStream is = part.getInputStream();
                OutputStream out = response.getOutputStream();
                fastStreamCopy(is, out);
                is.close();
                out.close();
                //if(!wasseen){
                //   if (us.isManualSeen()) {
                //   m.setFlag(Flags.Flag.SEEN, false);
                //   }
                //}
            }
            im.setPeek(false);

        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
        }
    }

    public void processGetAttachments(HttpServletRequest request, HttpServletResponse response) {
        MailAccount account = getAccount(request);
        String pfoldername = request.getParameter("folder");
        String puidmessage = request.getParameter("idmessage");
        String pids[] = request.getParameterValues("ids");
        String providername = request.getParameter("provider");
        String providerid = request.getParameter("providerid");

        try {
            account.checkStoreConnected();
            FolderCache mcache = null;
            Message m = null;
            if (providername == null) {
                mcache = account.getFolderCache(pfoldername);
                long newmsguid = Long.parseLong(puidmessage);
                m = mcache.getMessage(newmsguid);
            } else {
                mcache = fcProvided;
                m = mcache.getProvidedMessage(providername, providerid);
            }
            HTMLMailData mailData = mcache.getMailData((MimeMessage) m);
            String name = m.getSubject();
            if (name == null) {
                name = "attachments";
            }
            try {
                name = MailUtils.decodeQString(name);
            } catch (Exception exc) {
            }
            name += ".zip";
            //prepare hashmap to hold already used pnames
            HashMap<String, String> pnames = new HashMap<String, String>();
            ServletUtils.setFileStreamHeaders(response, "application/x-zip-compressed", DispositionType.INLINE,
                    name);
            JarOutputStream jos = new java.util.jar.JarOutputStream(response.getOutputStream());
            byte[] b = new byte[64 * 1024];
            for (String pid : pids) {
                Part part = mailData.getAttachmentPart(Integer.parseInt(pid));
                String pname = part.getFileName();
                if (pname == null) {
                    pname = "unknown";
                }
                /*
                try {
                   pname = MailUtils.decodeQString(pname, "iso-8859-1");
                } catch (Exception exc) {
                }
                */
                //keep name and extension
                String bpname = pname;
                String extpname = null;
                int ix = pname.lastIndexOf(".");
                if (ix > 0) {
                    bpname = pname.substring(0, ix);
                    extpname = pname.substring(ix + 1);
                }
                //check for existing pname and find an unused name
                int xid = 0;
                String rpname = pname;
                while (pnames.containsKey(rpname)) {
                    rpname = bpname + " (" + (++xid) + ")";
                    if (extpname != null)
                        rpname += "." + extpname;
                }

                JarEntry je = new JarEntry(rpname);
                jos.putNextEntry(je);
                if (providername == null) {
                    Folder folder = mailData.getFolder();
                    if (!folder.isOpen()) {
                        folder.open(Folder.READ_ONLY);
                    }
                }
                InputStream is = part.getInputStream();
                int len = 0;
                while ((len = is.read(b)) != -1) {
                    jos.write(b, 0, len);
                }
                is.close();

                //remember used pname
                pnames.put(rpname, rpname);
            }
            jos.closeEntry();
            jos.flush();
            jos.close();

        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
        }
    }

    public void processPreviewAttachment(HttpServletRequest request, HttpServletResponse response) {
        try {
            MailAccount account = getAccount(request);
            account.checkStoreConnected();
            String uploadId = request.getParameter("uploadId");
            /*String cid = request.getParameter("cid");
            Attachment att = null;
            if (tempname != null) {
               att = getAttachment(msgid, tempname);
            } else if (cid != null) {
               att = getAttachmentByCid(msgid, cid);
            }*/
            if (uploadId != null && hasUploadedFile(uploadId)) {
                WebTopSession.UploadedFile upl = getUploadedFile(uploadId);
                String ctype = ServletHelper.guessMediaType(upl.getFilename(), upl.getMediaType());
                ServletUtils.setFileStreamHeaders(response, ctype, DispositionType.INLINE, upl.getFilename());

                InputStream is = new FileInputStream(upl.getFile());
                OutputStream oout = response.getOutputStream();
                fastStreamCopy(is, oout);
            } else {
                Service.logger.debug("uploadId was not valid!");
            }
        } catch (Exception exc) {
            logger.error("Error in PreviewAttachment", exc);
        }
    }

    public void processDocPreviewAttachment(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        try {
            MailAccount account = getAccount(request);
            account.checkStoreConnected();
            String uploadId = request.getParameter("uploadId");
            /*String cid = request.getParameter("cid");
            Attachment att = null;
            if (tempname != null) {
               att = getAttachment(msgid, tempname);
            } else if (cid != null) {
               att = getAttachmentByCid(msgid, cid);
            }*/
            if (uploadId != null && hasUploadedFile(uploadId)) {
                WebTopSession.UploadedFile upl = getUploadedFile(uploadId);

                String fileHash = AlgoUtils.md5Hex(new CompositeId("previewattach", uploadId).toString());
                AttachmentViewerDocumentHandler docHandler = new AttachmentViewerDocumentHandler(false,
                        getEnv().getProfileId(), fileHash, upl.getFile());
                DocEditorManager.DocumentConfig config = getWts().prepareDocumentEditing(docHandler,
                        upl.getFilename(), upl.getFile().lastModified());

                new JsonResult(config).printTo(out);

            } else {
                Service.logger.debug("uploadId was not valid!");
            }
        } catch (Exception exc) {
            logger.error("Error in PreviewAttachment", exc);
        }
    }

    //view through onlyoffice doc viewer
    public void processViewAttachment(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String pfoldername = request.getParameter("folder");
        String puidmessage = request.getParameter("idmessage");
        String pidattach = request.getParameter("idattach");
        String providername = request.getParameter("provider");
        String providerid = request.getParameter("providerid");
        String pcid = request.getParameter("cid");
        String purl = request.getParameter("url");
        String punknown = request.getParameter("unknown");

        try {
            account.checkStoreConnected();
            FolderCache mcache = null;
            Message m = null;
            if (providername == null) {
                mcache = account.getFolderCache(pfoldername);
                long newmsguid = Long.parseLong(puidmessage);
                m = mcache.getMessage(newmsguid);
            } else {
                mcache = fcProvided;
                m = mcache.getProvidedMessage(providername, providerid);
            }
            HTMLMailData mailData = mcache.getMailData((MimeMessage) m);
            Part part = null;
            if (pcid != null) {
                part = mailData.getCidPart(pcid);
            } else if (purl != null) {
                part = mailData.getUrlPart(purl);
            } else if (pidattach != null) {
                part = mailData.getAttachmentPart(Integer.parseInt(pidattach));
            } else if (punknown != null) {
                part = mailData.getUnknownPart(Integer.parseInt(punknown));
            }

            if (part != null) {
                String name = part.getFileName();
                if (name == null)
                    name = "";
                try {
                    name = MailUtils.decodeQString(name);
                } catch (Exception exc) {
                }
                name = name.trim();
                if (providername == null) {
                    Folder folder = mailData.getFolder();
                    if (!folder.isOpen())
                        folder.open(Folder.READ_ONLY);
                }

                String fileHash = AlgoUtils.md5Hex(new CompositeId(pfoldername, puidmessage, pidattach).toString());
                long lastModified = m.getReceivedDate().getTime();
                AttachmentViewerDocumentHandler docHandler = new AttachmentViewerDocumentHandler(false,
                        getEnv().getProfileId(), fileHash, part, lastModified);
                DocEditorManager.DocumentConfig config = getWts().prepareDocumentEditing(docHandler, name,
                        lastModified);

                new JsonResult(config).printTo(out);

            }
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
        }
    }

    public java.util.Calendar convertTimeZone(String year, String month, String day, String hour, String min,
            String timezonefrom, String timezoneto) {
        java.util.TimeZone timeZone1 = java.util.TimeZone.getTimeZone(timezonefrom);
        java.util.TimeZone timeZone2 = java.util.TimeZone.getTimeZone(timezoneto);
        java.util.Calendar calendar = new GregorianCalendar();
        calendar.setTimeZone(timeZone1);
        calendar.set(Integer.parseInt(year), Integer.parseInt(month), Integer.parseInt(day), Integer.parseInt(hour),
                Integer.parseInt(min));
        java.util.Calendar calendarout = new GregorianCalendar();
        calendarout.setTimeZone(timeZone2);
        calendarout.setTimeInMillis(calendar.getTimeInMillis());
        return calendarout;
    }

    public void processCalendarRequest(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        MailAccount account = getAccount(request);
        String pcalaction = request.getParameter("calaction");
        String pfoldername = request.getParameter("folder");
        String puidmessage = request.getParameter("idmessage");
        String pidattach = request.getParameter("idattach");
        try {
            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(pfoldername);
            long newmsguid = Long.parseLong(puidmessage);
            Message m = mcache.getMessage(newmsguid);
            HTMLMailData mailData = mcache.getMailData((MimeMessage) m);
            Part part = mailData.getAttachmentPart(Integer.parseInt(pidattach));

            ICalendarRequest ir = new ICalendarRequest(part.getInputStream());
            ICalendarManager cm = (ICalendarManager) WT.getServiceManager("com.sonicle.webtop.calendar", true,
                    environment.getProfileId());
            if (pcalaction.equals("accept")) {
                Event ev = cm.addEventFromICal(cm.getBuiltInCalendar().getCalendarId(), ir.getCalendar());
                String ekey = cm.getEventInstanceKey(ev.getEventId());
                sendICalendarReply(account, ir, ((InternetAddress) m.getRecipients(RecipientType.TO)[0]),
                        PartStat.ACCEPTED);
                new JsonResult(ekey).printTo(out);

            } else if (pcalaction.equals("import")) {
                Event ev = cm.addEventFromICal(cm.getBuiltInCalendar().getCalendarId(), ir.getCalendar());
                String ekey = cm.getEventInstanceKey(ev.getEventId());
                new JsonResult(ekey).printTo(out);

            } else if (pcalaction.equals("cancel") || pcalaction.equals("update")) {
                cm.updateEventFromICal(ir.getCalendar());
                new JsonResult().printTo(out);

            } else {
                throw new Exception("Unsupported calendar request action : " + pcalaction);
            }
        } catch (Exception exc) {
            new JsonResult(exc).printTo(out);
            logger.error("Error sending " + pcalaction, exc);
        }
    }

    public void processDeclineInvitation(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        MailAccount account = getAccount(request);
        String pfoldername = request.getParameter("folder");
        String puidmessage = request.getParameter("idmessage");
        String pidattach = request.getParameter("idattach");
        try {
            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(pfoldername);
            long newmsguid = Long.parseLong(puidmessage);
            Message m = mcache.getMessage(newmsguid);
            HTMLMailData mailData = mcache.getMailData((MimeMessage) m);
            Part part = mailData.getAttachmentPart(Integer.parseInt(pidattach));

            ICalendarRequest ir = new ICalendarRequest(part.getInputStream());
            sendICalendarReply(account, ir, ((InternetAddress) m.getRecipients(RecipientType.TO)[0]),
                    PartStat.DECLINED);
            new JsonResult().printTo(out);
        } catch (Exception exc) {
            new JsonResult(false, exc.getMessage()).printTo(out);
            logger.error("Error sending decline", exc);
        }

    }

    /*   private void sendICalendarReply(InternetAddress forAddress, PartStat response, net.fortuna.ical4j.model.Calendar cal, String summary, String organizerAddress) throws Exception {
          UserProfile profile=environment.getProfile();
          Locale locale=profile.getLocale();
          String action_string=response.equals(PartStat.ACCEPTED)?
        lookupResource(locale, MailLocaleKey.ICAL_REPLY_ACCEPTED):
        lookupResource(locale, MailLocaleKey.ICAL_REPLY_DECLINED);
              
          net.fortuna.ical4j.model.Calendar reply=ICalendarUtils.buildInvitationReply(cal, ICalendarUtils.buildProdId(WT.getPlatformName()+" Mail"),forAddress, response);
              
          //If forAddress is not on any of the intended iCal attendee, don't send a reply (e.g. forwarded ics)
          if (reply!=null) {
     //String icalContent=reply.toString();
     String icalContent=ICalendarUtils.calendarToString(reply);
        
     String icalContentType="text/calendar; charset=UTF-8; method=REPLY";
     SimpleMessage smsg = new SimpleMessage(999999);
        
     String subject = action_string + " " + summary;
     smsg.setSubject(subject);
     InternetAddress to[]=new InternetAddress[1];
     to[0]=new InternetAddress(organizerAddress);
     smsg.setTo(to);
        
     //smsg.setContent(icalContent,"this is a meeting invitation",icalContentType);
     smsg.setContent("");
        
     javax.mail.internet.MimeBodyPart part2 = new javax.mail.internet.MimeBodyPart();
     part2.setContent(icalContent, MailUtils.buildPartContentType(icalContentType, "UTF-8"));
     part2.setHeader("Content-Transfer-Encoding", "8BIT");
     //part2.setFileName("webtop-reply.ics");
     //javax.mail.internet.MimeBodyPart part1 = new javax.mail.internet.MimeBodyPart();
     //part1.setText(content, "UTF8", "application/ics");
     //part1.setHeader("Content-type", "application/ics");
     //part1.setFileName("webtop-reply.ics");
        
     MimeMultipart mp = new MimeMultipart("mixed");
     mp.addBodyPart(part2);
     MimeBodyPart mbp=new MimeBodyPart();
     mbp.setHeader("Content-type", "multipart/mixed");
     mbp.setContent(mp);
        
     smsg.setAttachments(new javax.mail.Part[]{mbp});
        
     Exception exc=sendMsg(profile.getFullEmailAddress(), smsg, null);
     if (exc!=null) throw exc;
          }
              
       }*/

    private void sendICalendarReply(MailAccount account, ICalendarRequest request, InternetAddress forAddress,
            PartStat response) throws Exception {
        InternetAddress organizerAddress = InternetAddressUtils.toInternetAddress(request.getOrganizerAddress());
        sendICalendarReply(account, request.getCalendar(), organizerAddress, forAddress, response,
                request.getSummary());
    }

    private void sendICalendarReply(MailAccount account, net.fortuna.ical4j.model.Calendar ical,
            InternetAddress organizerAddress, InternetAddress forAddress, PartStat response, String eventSummary)
            throws Exception {
        String prodId = ICalendarUtils.buildProdId(WT.getPlatformName() + " Mail");
        net.fortuna.ical4j.model.Calendar icalReply = ICalendarUtils.buildInvitationReply(ical, prodId, forAddress,
                response);
        if (icalReply == null)
            throw new WTException("Unable to build ICalendar reply or maybe you are not into attendee list");

        // Creates base message parts
        net.fortuna.ical4j.model.property.Method icalMethod = net.fortuna.ical4j.model.property.Method.REPLY;
        String icalText = ICalendarUtils.calendarToString(icalReply);
        MimeBodyPart calPart = ICalendarUtils.createInvitationCalendarPart(icalMethod, icalText);
        String filename = ICalendarUtils.buildICalendarAttachmentFilename(WT.getPlatformName());
        MimeBodyPart attPart = ICalendarUtils.createInvitationAttachmentPart(icalText, filename);

        MimeMultipart mmp = ICalendarUtils.createInvitationPart(null, calPart, attPart);

        UserProfile.Data ud = WT.getUserData(getEnv().getProfileId());
        InternetAddress from = InternetAddressUtils.toInternetAddress(ud.getFullEmailAddress());
        Message message = createMessage(account, from,
                TplHelper.buildEventInvitationReplyEmailSubject(ud.getLocale(), response, eventSummary));
        message.addRecipient(RecipientType.TO, organizerAddress);
        message.setContent(mmp);

        sendMsg(message);
    }

    private MimeMessage createMessage(MailAccount account, InternetAddress from, String subject)
            throws MessagingException {
        try {
            subject = MimeUtility.encodeText(subject);
        } catch (Exception ex) {
        }

        MimeMessage message = new MimeMessage(account.getMailSession());
        message.setSubject(subject);
        message.addFrom(new InternetAddress[] { from });
        message.setSentDate(new java.util.Date());
        return message;
    }

    public void processUpdateCalendarReply(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        MailAccount account = getAccount(request);
        String pfoldername = request.getParameter("folder");
        String puidmessage = request.getParameter("idmessage");
        String pidattach = request.getParameter("idattach");
        //UserProfile profile=environment.getUserProfile();
        try {
            account.checkStoreConnected();
            FolderCache mcache = account.getFolderCache(pfoldername);
            long newmsguid = Long.parseLong(puidmessage);
            Message m = mcache.getMessage(newmsguid);
            HTMLMailData mailData = mcache.getMailData((MimeMessage) m);
            Part part = mailData.getAttachmentPart(Integer.parseInt(pidattach));

            ICalendarRequest ir = new ICalendarRequest(part.getInputStream());
            String event_id = null;
            //TODO: String event_id=((CalendarService)wts.getServiceByName("calendar")).updateFromReply(ir);
            if (event_id != null) {
                new JsonResult(event_id).printTo(out);
            } else {
                throw new Exception("Event not found");
            }
        } catch (Exception exc) {
            //sout="{\nresult: false, text:'"+Utils.jsEscape(exc.getMessage())+"'\n}";
            new JsonResult(false, exc.getMessage()).printTo(out);
            logger.error("Error getting calendar events", exc);
        }
    }

    boolean checkFileRules(String foldername) {
        try {
            List<MailFilter> filters = mailManager.getMailFilters(MailFiltersType.INCOMING, true);
            for (MailFilter filter : filters) {
                for (SieveAction action : filter.getSieveActions()) {
                    if (action.getMethod() == SieveActionMethod.FILE_INTO) {
                        if (StringUtils.equals(action.getArgument(), foldername))
                            return true;
                    }
                }
            }

        } catch (WTException ex) {
            logger.error("Error checking file action [{}]", ex, foldername);
        }
        return false;
    }

    boolean checkScanRules(String foldername) {
        boolean b = false;
        Connection con = null;
        try {
            UserProfile profile = environment.getProfile();
            con = getConnection();
            b = ScanDAO.getInstance().isScanFolder(con, profile.getDomainId(), profile.getUserId(), foldername);
        } catch (SQLException exc) {
            logger.error("Error checking Scan rules on folder {}", foldername, exc);
        } finally {
            DbUtils.closeQuietly(con);
        }
        return b;
    }

    public void processRunAdvancedSearch(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        try {
            if (ast != null && ast.isRunning()) {
                throw new Exception("Advanced search is still running!");
            }

            MailAccount account = getAccount(request);
            String folder = request.getParameter("folder");
            String strashspam = request.getParameter("trashspam");
            String ssubfolders = request.getParameter("subfolders");
            String sandor = request.getParameter("andor");
            String sentries[] = request.getParameterValues("entries");

            boolean subfolders = ssubfolders.equals("true");
            boolean trashspam = strashspam.equals("true");
            boolean and = sandor.equals("and");

            AdvancedSearchEntry entries[] = new AdvancedSearchEntry[sentries.length];
            for (int i = 0; i < sentries.length; ++i) {
                entries[i] = new AdvancedSearchEntry(sentries[i]);
            }

            if (folder.startsWith("folder:")) {
                folder = folder.substring(7);
                ast = new AdvancedSearchThread(this, account, folder, trashspam, subfolders, and, entries);
            } else {
                int folderType = folder.equals("personal") ? AdvancedSearchThread.FOLDERTYPE_PERSONAL
                        : folder.equals("shared") ? AdvancedSearchThread.FOLDERTYPE_SHARED
                                : AdvancedSearchThread.FOLDERTYPE_ALL;
                ast = new AdvancedSearchThread(this, account, folderType, trashspam, subfolders, and, entries);
            }
            ast.start();
            out.println("{\nresult: true\n}");
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            out.println("{\nresult: false, text:'" + StringEscapeUtils.escapeEcmaScript(exc.getMessage()) + "'\n}");
        }
    }

    public void processPollAdvancedSearch(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        CoreManager core = WT.getCoreManager();

        try {
            MailAccount account = getAccount(request);
            String sstart = request.getParameter("start");
            int start = 0;
            if (sstart != null) {
                start = Integer.parseInt(sstart);
            }
            String sout = "{\n";
            if (ast != null) {
                UserProfile profile = environment.getProfile();
                Locale locale = profile.getLocale();
                java.util.Calendar cal = java.util.Calendar.getInstance(locale);
                ArrayList<Message> msgs = ast.getResult();
                int totalrows = msgs.size();
                int newrows = totalrows - start;
                sout += "total:" + totalrows + ",\nstart:" + start + ",\nlimit:" + newrows + ",\nmessages: [\n";
                boolean first = true;
                for (int i = start; i < msgs.size(); ++i) {
                    Message xm = msgs.get(i);
                    if (xm.isExpunged()) {
                        continue;
                    }
                    IMAPFolder xmfolder = (IMAPFolder) xm.getFolder();
                    boolean wasopen = xmfolder.isOpen();
                    if (!wasopen)
                        xmfolder.open(Folder.READ_ONLY);
                    long nuid = xmfolder.getUID(xm);
                    if (!wasopen)
                        xmfolder.close(false);
                    IMAPMessage m = (IMAPMessage) xm;
                    //Date
                    java.util.Date d = m.getSentDate();
                    if (d == null) {
                        d = m.getReceivedDate();
                    }
                    if (d == null) {
                        d = new java.util.Date(0);
                    }
                    cal.setTime(d);
                    int yyyy = cal.get(java.util.Calendar.YEAR);
                    int mm = cal.get(java.util.Calendar.MONTH);
                    int dd = cal.get(java.util.Calendar.DAY_OF_MONTH);
                    int hhh = cal.get(java.util.Calendar.HOUR_OF_DAY);
                    int mmm = cal.get(java.util.Calendar.MINUTE);
                    int sss = cal.get(java.util.Calendar.SECOND);
                    String xfolder = xm.getFolder().getFullName();
                    FolderCache fc = account.getFolderCache(xfolder);
                    String folder = StringEscapeUtils.escapeEcmaScript(xfolder);
                    String foldername = StringEscapeUtils
                            .escapeEcmaScript(MailUtils.htmlescape(getInternationalFolderName(fc)));
                    //From
                    String from = "";
                    Address ia[] = m.getFrom();
                    if (ia != null) {
                        InternetAddress iafrom = (InternetAddress) ia[0];
                        from = iafrom.getPersonal();
                        if (from == null) {
                            from = iafrom.getAddress();
                        }
                    }
                    from = (from == null ? "" : StringEscapeUtils.escapeEcmaScript(MailUtils.htmlescape(from)));
                    //To
                    String to = "";
                    ia = m.getRecipients(Message.RecipientType.TO);
                    if (ia != null) {
                        InternetAddress iato = (InternetAddress) ia[0];
                        to = iato.getPersonal();
                        if (to == null) {
                            to = iato.getAddress();
                        }
                    }
                    to = (to == null ? "" : StringEscapeUtils.escapeEcmaScript(MailUtils.htmlescape(to)));
                    //Subject
                    String subject = m.getSubject();
                    if (subject != null) {
                        try {
                            subject = MailUtils.decodeQString(subject);
                        } catch (Exception exc) {

                        }
                    }
                    subject = (subject == null ? ""
                            : StringEscapeUtils.escapeEcmaScript(MailUtils.htmlescape(subject)));
                    //Unread
                    boolean unread = !m.isSet(Flags.Flag.SEEN);
                    //if (ppattern==null && unread) ++funread;
                    //Priority
                    int priority = getPriority(m);
                    //Status
                    java.util.Date today = new java.util.Date();
                    java.util.Calendar cal1 = java.util.Calendar.getInstance(locale);
                    java.util.Calendar cal2 = java.util.Calendar.getInstance(locale);
                    boolean isToday = false;
                    if (d != null) {
                        cal1.setTime(today);
                        cal2.setTime(d);
                        if (cal1.get(java.util.Calendar.DAY_OF_MONTH) == cal2.get(java.util.Calendar.DAY_OF_MONTH)
                                && cal1.get(java.util.Calendar.MONTH) == cal2.get(java.util.Calendar.MONTH)
                                && cal1.get(java.util.Calendar.YEAR) == cal2.get(java.util.Calendar.YEAR)) {
                            isToday = true;
                        }
                    }

                    Flags flags = m.getFlags();
                    String status = "read";
                    if (flags != null) {
                        if (flags.contains(Flags.Flag.ANSWERED)) {
                            if (flags.contains("$Forwarded")) {
                                status = "repfwd";
                            } else {
                                status = "replied";
                            }
                        } else if (flags.contains("$Forwarded")) {
                            status = "forwarded";
                        } else if (flags.contains(Flags.Flag.SEEN)) {
                            status = "read";
                        } else if (isToday) {
                            status = "new";
                        } else {
                            status = "unread";
                        }
                        //                    if (flags.contains(Flags.Flag.USER)) flagImage=webtopapp.getUri()+"/images/themes/"+profile.getTheme()+"/mail/flag.gif";
                    }
                    //Size
                    int msgsize = 0;
                    msgsize = (m.getSize() * 3) / 4;// /1024 + 1;
                    //User flags
                    String cflag = "";
                    for (WebtopFlag webtopFlag : webtopFlags) {
                        String flagstring = webtopFlag.label;
                        //String tbflagstring=webtopFlag.tbLabel;
                        if (!flagstring.equals("complete")) {
                            String oldflagstring = "flag" + flagstring;
                            if (flags.contains(flagstring) || flags.contains(oldflagstring)
                            /*|| (tbflagstring!=null && flags.contains(tbflagstring))*/
                            ) {
                                cflag = flagstring;
                            }
                        }
                    }
                    boolean flagComplete = flags.contains("complete");
                    if (flagComplete) {
                        cflag += "-complete";
                    }

                    //idmessage=idmessage.replaceAll("\\\\", "\\\\");
                    //idmessage=OldUtils.jsEscape(idmessage);
                    if (!first) {
                        sout += ",\n";
                    }
                    boolean archived = false;
                    if (hasDmsDocumentArchiving()) {
                        archived = m.getHeader("X-WT-Archived") != null;
                        if (!archived) {
                            archived = flags.contains(sflagDmsArchived);
                        }
                    }

                    boolean hasNote = flags.contains(sflagNote);

                    sout += "{folder:'" + folder + "', folderdesc:'" + foldername + "',idmandfolder:'" + folder
                            + "|" + nuid + "',idmessage:'" + nuid + "',priority:" + priority + ",status:'" + status
                            + "',to:'" + to + "',from:'" + from + "',subject:'" + subject + "',date: new Date("
                            + yyyy + "," + mm + "," + dd + "," + hhh + "," + mmm + "," + sss + "),unread: " + unread
                            + ",size:" + msgsize + ",flag:'" + cflag + "'" + (archived ? ",arch:true" : "")
                            + (isToday ? ",istoday:true" : "") + (hasNote ? ",note:true" : "") + "}";
                    first = false;
                }
                sout += "\n]\n, progress: " + ast.getProgress() + ", curfoldername: '"
                        + StringEscapeUtils.escapeEcmaScript(getInternationalFolderName(ast.getCurrentFolder()))
                        + "', " + "max: " + ast.isMoreThanMax() + ", finished: "
                        + (ast.isFinished() || ast.isCanceled() || !ast.isRunning()) + " }\n";
            } else {
                sout += "total:0,\nstart:0,\nlimit:0,\nmessages: [\n";
                sout += "\n] }\n";
            }
            out.println(sout);
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
        }
    }

    public void processCancelAdvancedSearch(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        if (ast != null && ast.isRunning()) {
            ast.cancel();
        }
        out.println("{\nresult: true\n}");
    }

    public void processRunSmartSearch(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        try {
            if (sst != null && sst.isRunning())
                sst.cancel();

            UserProfile profile = environment.getProfile();
            MailAccount account = getAccount(request);
            String pattern = ServletUtils.getStringParameter(request, "pattern", true);
            String folder = ServletUtils.getStringParameter(request, "folder", false);
            boolean trashspam = ServletUtils.getBooleanParameter(request, "trashspam", false);
            boolean fromme = ServletUtils.getBooleanParameter(request, "fromme", false);
            boolean tome = ServletUtils.getBooleanParameter(request, "tome", false);
            boolean attachments = ServletUtils.getBooleanParameter(request, "attachments", false);
            int year = ServletUtils.getIntParameter(request, "year", 0);
            int month = ServletUtils.getIntParameter(request, "month", 0);
            int day = ServletUtils.getIntParameter(request, "day", 0);
            ArrayList<String> ispersonfilters = ServletUtils.getStringParameters(request, "ispersonfilters");
            ArrayList<String> isnotpersonfilters = ServletUtils.getStringParameters(request, "isnotpersonfilters");
            ArrayList<String> isfolderfilters = ServletUtils.getStringParameters(request, "isfolderfilters");
            ArrayList<String> isnotfolderfilters = ServletUtils.getStringParameters(request, "isnotfolderfilters");
            Set<String> _folderIds = account.getFolderCacheKeys();

            ArrayList<SearchTerm> terms = new ArrayList<>();
            SearchTerm searchTerm = null;

            //sort folders, placing first interesting ones
            ArrayList<String> folderIds = new ArrayList<>();
            Collections.sort(folderIds);
            String firstFolders[] = { account.getInboxFolderFullName(), account.getFolderSent() };
            for (String folderId : firstFolders)
                folderIds.add(folderId);
            for (String folderId : _folderIds) {

                //if folder selected, look only under that folder
                if (folder != null && folder.trim().length() > 0) {
                    if (!folder.equals(folderId) && !account.isUnderFolder(folder, folderId))
                        continue;
                } else {
                    //else skip shared
                    if (account.isUnderSharedFolder(folderId))
                        continue;
                }

                //skip trash & spam unless selected
                if (!trashspam && (account.isTrashFolder(folderId) || account.isSpamFolder(folderId)))
                    continue;

                boolean skip = false;
                for (String skipfolder : firstFolders) {
                    if (skipfolder.equals(folderId)) {
                        skip = true;
                        break;
                    }
                }
                if (!skip)
                    folderIds.add(folderId);
            }

            terms.add(new OrTerm(ImapQuery.toAnySearchTerm(pattern)));
            int n = terms.size();

            if (n == 1)
                searchTerm = terms.get(0);
            else if (n > 1) {
                SearchTerm vterms[] = new SearchTerm[n];
                terms.toArray(vterms);
                searchTerm = new AndTerm(vterms);
            }

            sst = new SmartSearchThread(this, account, folderIds, fromme, tome, attachments, ispersonfilters,
                    isnotpersonfilters, isfolderfilters, isnotfolderfilters, year, month, day, searchTerm, false);
            sst.start();
            new JsonResult().printTo(out);
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            new JsonResult(false, exc.getMessage()).printTo(out);
        }
    }

    public void processPollSmartSearch(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        try {
            if (sst != null) {
                JsSmartSearchTotals jssst = sst.getSmartSearchTotals();
                synchronized (jssst.lock) {
                    JsonResult jsr = new JsonResult(jssst);
                    jsr.printTo(out);
                }
            } else
                new JsonResult(false, "Smart search is not available").printTo(out);
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            new JsonResult(false, exc.getMessage()).printTo(out);
        }
    }

    public void processPortletRunSearch(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        try {
            UserProfile profile = environment.getProfile();
            boolean trash = false;
            boolean spam = false;
            ArrayList<SearchTerm> terms = new ArrayList<SearchTerm>();
            SearchTerm searchTerm = null;

            if (pst != null && pst.isRunning())
                pst.cancel();

            MailAccount account = getAccount(request);

            String pattern = ServletUtils.getStringParameter(request, "pattern", true);

            Set<String> _folderIds = account.getFolderCacheKeys();

            //sort folders, placing first interesting ones
            ArrayList<String> folderIds = new ArrayList<>();
            Collections.sort(folderIds);
            String firstFolders[] = { account.getInboxFolderFullName(), account.getFolderSent() };
            for (String folderId : firstFolders)
                folderIds.add(folderId);
            for (String folderId : _folderIds) {

                if (account.isUnderSharedFolder(folderId))
                    continue;
                //skip trash & spam unless selected
                if (!trash && account.isTrashFolder(folderId))
                    continue;
                if (!spam && account.isSpamFolder(folderId))
                    continue;

                folderIds.add(folderId);
            }

            terms.add(new OrTerm(ImapQuery.toAnySearchTerm(pattern)));
            int n = terms.size();

            if (n == 1)
                searchTerm = terms.get(0);
            else if (n > 1) {
                SearchTerm vterms[] = new SearchTerm[n];
                terms.toArray(vterms);
                searchTerm = new AndTerm(vterms);
            }
            pst = new PortletSearchThread(this, account, folderIds, searchTerm, false);
            pst.start();
            new JsonResult().printTo(out);
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            new JsonResult(false, exc.getMessage()).printTo(out);
        }
    }

    public void processPortletPollSearch(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        try {
            if (pst != null) {
                JsPortletSearchResult jspsr = pst.getPortletSearchResult();
                synchronized (jspsr.lock) {
                    JsonResult jsr = new JsonResult(jspsr);
                    jsr.printTo(out);
                }
            } else
                new JsonResult(false, "Portlet search is not available").printTo(out);
        } catch (Exception exc) {
            Service.logger.error("Exception", exc);
            new JsonResult(false, exc.getMessage()).printTo(out);
        }
    }

    public void processSetMessageView(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        try {
            String region = ServletUtils.getStringParameter(request, "region", true);
            Integer width = ServletUtils.getIntParameter(request, "width", true);
            Integer height = ServletUtils.getIntParameter(request, "height", true);
            Boolean collapsed = ServletUtils.getBooleanParameter(request, "collapsed", false);

            us.setMessageViewRegion(region);
            us.setMessageViewWidth(width);
            us.setMessageViewHeight(height);
            us.setMessageViewCollapsed(collapsed);

            new JsonResult().printTo(out);

        } catch (Exception ex) {
            logger.error("Error executing action SetToolComponentWidth", ex);
            new JsonResult(false, "Unable to save with").printTo(out);
        }
    }

    public boolean isSharedSeen(MailAccount account) throws MessagingException {
        if (!account.hasAnnotations())
            return false;

        SonicleIMAPFolder xfolder = (SonicleIMAPFolder) account.getFolder("INBOX");
        String annot = xfolder.getAnnotation("/vendor/cmu/cyrus-imapd/sharedseen", true);
        return annot.equals("true");
    }

    public void setSharedSeen(MailAccount account, boolean b) throws MessagingException {
        if (!account.hasAnnotations())
            return;
        SonicleIMAPFolder xfolder = (SonicleIMAPFolder) account.getFolder("INBOX");
        xfolder.setAnnotation("/vendor/cmu/cyrus-imapd/sharedseen", true, b ? "true" : "false");
    }

    protected boolean schemeWantsUserWithDomain(AuthenticationDomain ad) {
        String scheme = ad.getDirUri().getScheme();
        //return scheme.equals("ldapneth")?false:scheme.equals("ad")?true:scheme.startsWith("ldap");
        return scheme.equals("ad") || scheme.startsWith("ldap");
    }

    UserProfileId aclUserIdToUserId(String aclUserId) {
        String userId = aclUserId;
        //imap user includes domain only if ldap or AD, not including nethserver 6
        //strip domain if needed
        Principal principal = (Principal) RunContext.getSubject().getPrincipal();
        AuthenticationDomain ad = principal.getAuthenticationDomain();
        if (schemeWantsUserWithDomain(ad)) {
            int ix = aclUserId.indexOf("@");
            if (ix > 0) {
                String domain = aclUserId.substring(ix + 1).toLowerCase();
                if (ad.getInternetName().toLowerCase().equals(domain))
                    userId = aclUserId.substring(0, ix);
                else {
                    //skip if non domain users not permitted
                    if (!RunContext.isPermitted(true, SERVICE_ID, "SHARING_UNKNOWN_ROLES", "SHOW"))
                        userId = null;
                }
            } else {
                if (!RunContext.isPermitted(true, SERVICE_ID, "SHARING_UNKNOWN_ROLES", "SHOW"))
                    userId = null;
            }
        }
        if (principal.getUserId().equals(userId))
            userId = null;

        UserProfileId pid = null;
        if (userId != null)
            pid = new UserProfileId(environment.getProfile().getDomainId(), userId);
        return pid;
    }

    public void processManageSharing(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        Connection con = null;
        try {
            con = getConnection();
            MailAccount account = getAccount(request);
            String crud = ServletUtils.getStringParameter(request, "crud", true);
            String id = null;
            Payload<MapItem, JsSharing> pl = null;

            if (crud.equals(Crud.READ)) {
                id = ServletUtils.getStringParameter(request, "id", true);
            } else if (crud.equals(Crud.UPDATE)) {
                pl = ServletUtils.getPayload(request, JsSharing.class);
                id = pl.data.id;
            }

            FolderCache fc = account.getFolderCache(id);
            SonicleIMAPFolder folder = (SonicleIMAPFolder) fc.getFolder();
            CoreManager core = WT.getCoreManager();
            Sharing wtsharing = core.getSharing(SERVICE_ID, MailManager.IDENTITY_SHARING_GROUPNAME,
                    MailManager.IDENTITY_SHARING_ID);
            String description = folder.getName();
            ArrayList<JsSharing.SharingRights> rights = new ArrayList<>();
            for (ACL acl : folder.getACL()) {
                String aclUserId = acl.getName();
                UserProfileId pid = aclUserIdToUserId(aclUserId);
                if (pid == null)
                    continue;
                String roleUid = core.getUserUid(pid);
                String roleDescription = null;
                boolean shareIdentity = false;
                boolean forceMailcard = false;
                if (roleUid == null) {
                    if (!RunContext.isPermitted(true, SERVICE_ID, "SHARING_UNKNOWN_ROLES", "SHOW"))
                        continue;
                    roleUid = aclUserId;
                    roleDescription = roleUid;
                } else {
                    Sharing.RoleRights wtrr = wtsharing.getRoleRights(roleUid);
                    if (wtrr != null) {
                        shareIdentity = wtrr.folderRead;
                        forceMailcard = wtrr.folderUpdate;
                    }
                    String dn;
                    try {
                        OUser ouser = core.getUser(pid);
                        dn = ouser.getDisplayName();
                    } catch (WTException exc) {
                        dn = "no description available";
                    }
                    roleDescription = pid.getUserId() + " [" + dn + "]";
                }

                Rights ar = acl.getRights();
                rights.add(new JsSharing.SharingRights(id, roleUid, roleDescription, aclUserId, shareIdentity,
                        forceMailcard, ar.contains(Rights.Right.getInstance('l')),
                        ar.contains(Rights.Right.getInstance('r')), ar.contains(Rights.Right.getInstance('s')),
                        ar.contains(Rights.Right.getInstance('w')), ar.contains(Rights.Right.getInstance('i')),
                        ar.contains(Rights.Right.getInstance('p')), ar.contains(Rights.Right.getInstance('k')),
                        ar.contains(Rights.Right.getInstance('a')), ar.contains(Rights.Right.getInstance('x')),
                        ar.contains(Rights.Right.getInstance('t')), ar.contains(Rights.Right.getInstance('n')),
                        ar.contains(Rights.Right.getInstance('e'))));
            }
            JsSharing sharing = new JsSharing(id, description, "this", rights);

            if (crud.equals(Crud.READ)) {
                new JsonResult(sharing).printTo(out);
            } else if (crud.equals(Crud.UPDATE)) {
                for (JsSharing.SharingRights sr : pl.data.rights) {
                    //try to fill in the imapId where empty
                    if (StringUtils.isEmpty(sr.imapId)) {
                        UserProfileId pid = core.userUidToProfileId(sr.roleUid);
                        String imapId = null;
                        //look for any custom mail user
                        OUserMap userMap = UserMapDAO.getInstance().selectById(con, pid.getDomainId(),
                                pid.getUserId());
                        if (userMap != null && !StringUtils.isEmpty(userMap.getMailUser())) {
                            if (userMap.getMailHost().equals(account.getHost())
                                    && userMap.getMailPort().equals(account.getPort())
                                    && userMap.getMailProtocol().equals(account.getProtocol())) {
                                imapId = userMap.getMailUser();
                            }
                        } else {
                            imapId = pid.getUserId();
                            Principal principal = (Principal) RunContext.getSubject().getPrincipal();
                            AuthenticationDomain ad = principal.getAuthenticationDomain();
                            if (schemeWantsUserWithDomain(ad)) {
                                imapId += "@" + ad.getInternetName();
                            }
                        }
                        sr.imapId = imapId;
                    }
                }

                String foldername = pl.data.method.equals("all") ? "" : id;
                boolean recursive = pl.data.method.equals("all") || pl.data.method.equals("branch");

                //
                //Prepare new sharing structure
                //

                Sharing newwtsharing = new Sharing();
                newwtsharing.setId(MailManager.IDENTITY_SHARING_ID);
                ArrayList<Sharing.RoleRights> newwtrights = new ArrayList<>();

                //first delete all removed roles
                for (JsSharing.SharingRights sr : sharing.rights) {
                    String imapId = ss.isImapAclLowercase() ? sr.imapId.toLowerCase() : sr.imapId;
                    if (!pl.data.hasImapId(sr.imapId)) {
                        logger.debug("Folder [" + foldername + "] - remove acl for " + imapId + " recursive="
                                + recursive);
                        account.removeFolderSharing(foldername, imapId, recursive);
                        updateIdentitySharingRights(newwtrights, sr.roleUid, false, false);
                    }
                }

                //now apply new acls
                for (JsSharing.SharingRights sr : pl.data.rights) {
                    String imapId = ss.isImapAclLowercase() ? sr.imapId.toLowerCase() : sr.imapId;
                    if (!StringUtils.isEmpty(sr.imapId)) {
                        String srights = sr.toString();
                        logger.debug("Folder [" + foldername + "] - add acl " + srights + " for " + imapId
                                + " recursive=" + recursive);
                        account.setFolderSharing(foldername, imapId, srights, recursive);
                        updateIdentitySharingRights(newwtrights, sr.roleUid, sr.shareIdentity, sr.forceMailcard);
                    }
                }

                newwtsharing.setRights(newwtrights);
                WT.getCoreManager().updateSharing(SERVICE_ID, MailManager.IDENTITY_SHARING_GROUPNAME, newwtsharing);

                new JsonResult().printTo(out);
            }

        } catch (Exception ex) {
            logger.error("Error in action ManageSharing", ex);
            new JsonResult(false, "Error").printTo(out);
        } finally {
            DbUtils.closeQuietly(con);
        }
    }

    private void updateIdentitySharingRights(ArrayList<Sharing.RoleRights> wtrights, String roleUid,
            boolean shareIdentity, boolean forceMailcard) throws WTException {
        //update sharing settings
        SharePermsFolder spf = new SharePermsFolder();
        if (shareIdentity || forceMailcard) {
            if (shareIdentity)
                spf.add(SharePermsFolder.READ);
            if (forceMailcard)
                spf.add(SharePermsFolder.UPDATE);
            wtrights.add(new Sharing.RoleRights(roleUid, null, spf, new SharePermsElements()));
        }
    }

    public SharedPrincipal getSharedPrincipal(String domainId, String mailUserId) {
        SharedPrincipal p = null;
        Connection con = null;
        try {
            con = getConnection();
            logger.debug("looking for shared folder map on {}@{}", mailUserId, domainId);
            OUserMap omap = UserMapDAO.getInstance().selectFirstByMailUser(con, domainId, mailUserId);
            OUser ouser;
            if (omap != null) {
                logger.debug("found mapping : {}", omap.getUserId());
                //get mapped webtop user
                ouser = UserDAO.getInstance().selectByDomainUser(con, domainId, omap.getUserId());
            } else {
                //remove @domain if present
                mailUserId = StringUtils.substringBefore(mailUserId, "@");
                logger.debug("mapping not found, looking for a webtop user with id = {}", mailUserId);
                //try looking for a webtop user with userId=mailUserId
                ouser = UserDAO.getInstance().selectByDomainUser(con, domainId, mailUserId);
            }

            String desc = null;
            if (ouser != null) {
                desc = LangUtils.value(ouser.getDisplayName(), "");
            } else {
                String email = mailUserId;
                if (email.indexOf("@") < 0)
                    email += "@" + WT.getDomainInternetName(domainId);
                UserProfile.Data udata = WT.guessUserData(email);
                if (udata != null)
                    desc = LangUtils.value(udata.getDisplayName(), "");
            }

            if (desc != null) {
                logger.debug("webtop user found, desc={}", desc);
                p = new SharedPrincipal(mailUserId, desc.trim());
            } else {
                logger.debug("webtop user not found, creating unmapped principal");
                p = new SharedPrincipal(mailUserId, mailUserId);
            }

        } catch (SQLException exc) {
            logger.error("Error finding principal for {}@{}", mailUserId, domainId, exc);
        } finally {
            DbUtils.closeQuietly(con);
        }
        return p;
    }

    private void createFile(InputStream in, File outfile) throws IOException {
        FileOutputStream out = new FileOutputStream(outfile);
        byte buffer[] = new byte[8192];
        int n = 0;
        while ((n = in.read(buffer)) >= 0) {
            if (n > 0) {
                out.write(buffer, 0, n);
            }
        }
        out.close();
    }

    private void fastStreamCopy(final InputStream src, final OutputStream dest) throws IOException {
        final ReadableByteChannel in = Channels.newChannel(src);
        final WritableByteChannel out = Channels.newChannel(dest);
        fastChannelCopy(in, out);
        in.close();
        out.close();
    }

    public static void fastChannelCopy(final ReadableByteChannel src, final WritableByteChannel dest)
            throws IOException {
        final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
        while (src.read(buffer) != -1) {
            // prepare the buffer to be drained
            buffer.flip();
            // write to the channel, may block
            dest.write(buffer);
            // If partial transfer, shift remainder down
            // If buffer is empty, same as doing clear()
            buffer.compact();
        }
        // EOF will leave buffer in fill state
        buffer.flip();
        // make sure the buffer is fully drained.
        while (buffer.hasRemaining()) {
            dest.write(buffer);
        }
    }

    @Override
    public ServiceVars returnServiceVars() {
        ServiceVars co = new ServiceVars();
        Connection con = null;
        try {
            con = getConnection();
            List<Identity> identities = mailManager.listIdentities();

            List<Identity> jsidents = new ArrayList();
            jsidents.addAll(identities);
            for (Identity jsid : jsidents) {
                //if (jsid.isType(Identity.TYPE_AUTO)) {
                //   jsid.setMainFolder(getSharedFolderName(jsid.getOriginPid().getUserId(), "INBOX"));
                //}
                if (jsid.isMainIdentity())
                    loadMainIdentityMailcard(jsid);
                else
                    loadIdentityMailcard(jsid);
            }

            if (mainAccount.hasDifferentDefaultFolder())
                co.put("inboxFolder", mainAccount.getInboxFolderFullName());
            co.put("attachmentMaxFileSize", ss.getAttachmentMaxFileSize(true));
            co.put("folderDrafts", us.getFolderDrafts());
            co.put("folderSent", us.getFolderSent());
            co.put("folderSpam", us.getFolderSpam());
            co.put("folderTrash", us.getFolderTrash());
            co.put("folderArchive", us.getFolderArchive());
            co.put("folderSeparator", mainAccount.getFolderSeparator());
            co.put("format", us.getFormat());
            co.put("fontName", us.getFontName());
            co.put("fontSize", us.getFontSize());
            co.put("fontColor", us.getFontColor());
            co.put("receipt", us.isReceipt());
            co.put("autoAddContact", us.isAutoAddContact());
            co.put("priority", us.isPriority());
            co.put("viewMode", us.getViewMode());
            co.put("noMailcardOnReplyForward", us.isNoMailcardOnReplyForward());
            co.put("pageRows", us.getPageRows());
            co.put("schedDisabled", ss.isScheduledEmailsDisabled());
            co.put("identities", jsidents);
            co.put("manualSeen", us.isManualSeen());
            co.put("seenOnOpen", us.isSeenOnOpen());
            co.put("messageViewRegion", us.getMessageViewRegion());
            co.put("messageViewWidth", us.getMessageViewWidth());
            co.put("messageViewHeight", us.getMessageViewHeight());
            co.put("messageViewCollapsed", us.getMessageViewCollapsed());
            co.put("messageViewMaxTos", ss.getMessageViewMaxTos());
            co.put("messageViewMaxCcs", ss.getMessageViewMaxCcs());
            co.put("messageEditSubject", ss.isMessageEditSubject());
            co.put("columnSizes", JsonResult.gson.toJson(us.getColumnSizes()));
            co.put("autoResponderActive", mailManager.isAutoResponderActive());
            co.put("showUpcomingEvents", us.getShowUpcomingEvents());
            co.put("showUpcomingTasks", us.getShowUpcomingTasks());
            co.put("isArchivingExternal", ss.isArchivingExternal());
            co.put("todayRowColor", us.getTodayRowColor());

            if (RunContext.isPermitted(true, SERVICE_ID, "FAX", "ACCESS")) {
                co.put("faxSubject", getEnv().getCoreServiceSettings().getFaxSubject());
            }

            List<MailSettings.ExternalProvider> providers = ss.getExternalProviders();
            HashMap<String, String> providerIcons = new HashMap<>();
            for (MailSettings.ExternalProvider provider : providers)
                providerIcons.put(provider.id, provider.iconUrl);

            String extids = "";
            for (MailAccount acct : externalAccounts) {
                String id = acct.getId();
                if (extids.length() > 0)
                    extids += ",";
                extids += id;

                co.put("externalAccountDescription." + id, externalAccountsMap.get(id).getAccountDescription());
                co.put("externalAccountDrafts." + id, externalAccountsMap.get(id).getFolderDrafts());
                co.put("externalAccountSent." + id, externalAccountsMap.get(id).getFolderSent());
                co.put("externalAccountTrash." + id, externalAccountsMap.get(id).getFolderTrash());
                co.put("externalAccountSpam." + id, externalAccountsMap.get(id).getFolderSpam());
                co.put("externalAccountReadOnly." + id, externalAccountsMap.get(id).isReadOnly());
                co.put("externalAccountIcon." + id, providerIcons.get(externalAccountsMap.get(id).getProviderId()));
            }
            co.put("externalAccounts", extids);

            if (ss.isToolbarCompact())
                co.put("toolbarCompact", true);

        } catch (Exception ex) {
            logger.error("Error getting client options", ex);
        } finally {
            DbUtils.closeQuietly(con);
        }
        return co;
    }

    private void loadMainIdentityMailcard(Identity id) {
        Mailcard mc = mailManager.getMailcard();
        try {
            UserProfile.PersonalInfo upi = WT.getCoreManager()
                    .getUserPersonalInfo(mailManager.getTargetProfileId());
            mc.substitutePlaceholders(upi);
        } catch (WTException exc) {
            logger.error("cannot load user personal info", exc);
        }
        //mc.html=LangUtils.stripLineBreaks(mc.html);
        id.setMailcard(mc);
    }

    private void loadIdentityMailcard(Identity id) {
        Mailcard mc = mailManager.getMailcard(id);
        if (mc != null) {
            if (id.isType(Identity.TYPE_AUTO)) {
                UserProfileId opid = id.getOriginPid();
                // In case of auto identities we need to build real mainfolder
                try {
                    String mailUser = getMailUsername(opid);
                    String mainfolder = getSharedFolderName(getAccount(id), mailUser,
                            "INBOX" /*id.getMainFolder()*/);
                    id.setMainFolder(mainfolder);
                    if (mainfolder == null)
                        throw new Exception(MessageFormat.format("Shared folderName is null [{0}, {1}]", mailUser,
                                id.getMainFolder()));
                } catch (Exception ex) {
                    logger.error("Unable to get auto identity foldername [{}]", id.getEmail(), ex);
                }

                /*
                // Avoids default mailcard display for automatic identities
                if(mc.source.equals(Mailcard.TYPE_DEFAULT) && !StringUtils.isEmpty(id.getMainFolder())) {
                   mc = getMailcard();
                }*/

                if (!id.isForceMailcard())
                    mc = mailManager.getMailcard();

                try {
                    UserProfile.PersonalInfo upi = WT.getCoreManager().getUserPersonalInfo(opid);
                    mc.substitutePlaceholders(upi);
                } catch (Exception ex) {
                    logger.error("Unable to get auto identity personal info [{}]", id.getEmail(), ex);
                }
            }
        }
        //mc.html=LangUtils.stripLineBreaks(mc.html);
        id.setMailcard(mc);
    }

    private String getMailUsername(UserProfileId userProfileId) {
        Connection con = null;
        String username = null;
        try {
            con = WT.getConnection(SERVICE_ID);
            OUserMap omap = UserMapDAO.getInstance().selectById(con, userProfileId.getDomainId(),
                    userProfileId.getUserId());
            if (omap != null)
                username = omap.getMailUser();
            if (username == null || username.isEmpty())
                username = userProfileId.getUserId();
        } catch (Exception exc) {
            logger.error("Error mapping mail user", exc);
        } finally {
            DbUtils.closeQuietly(con);
        }
        return username + "@" + WT.getDomainInternetName(userProfileId.getDomainId());
    }

    public void processLookupSieveScripts(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        ArrayList<JsSimple> items = new ArrayList<>();

        try {
            items.add(new JsSimple(MailManager.SIEVE_WEBTOP_SCRIPT, MailManager.SIEVE_WEBTOP_SCRIPT));

            try {
                List<com.fluffypeople.managesieve.SieveScript> scripts = mailManager.listSieveScripts();
                for (com.fluffypeople.managesieve.SieveScript script : scripts) {
                    // Skip new webtop script name, we want it as first element
                    if (!StringUtils.equals(script.getName(), MailManager.SIEVE_WEBTOP_SCRIPT)) {
                        items.add(new JsSimple(script.getName(), script.getName()));
                    }
                }
            } catch (WTException ex1) {
                logger.warn("Unable to read scripts", ex1);
            }

            new JsonResult(items, items.size()).printTo(out);

        } catch (Exception ex) {
            logger.error("Error in LookupSieveScripts", ex);
            new JsonResult(false, ex.getMessage()).printTo(out);
        }
    }

    public void processManageMailFilters(HttpServletRequest request, HttpServletResponse response,
            PrintWriter out) {
        try {
            String crud = ServletUtils.getStringParameter(request, "crud", true);
            DateTimeZone profileTz = getEnv().getProfile().getTimeZone();

            if (crud.equals(Crud.READ)) {
                String id = ServletUtils.getStringParameter(request, "id", true);

                int scriptCount = -1;
                String activeScript = null;
                try {
                    List<com.fluffypeople.managesieve.SieveScript> scripts = mailManager.listSieveScripts();
                    scriptCount = scripts.size();
                    activeScript = findActiveScriptName(scripts);
                } catch (WTException ex1) {
                    logger.warn("Error reading active script", ex1);
                }
                if (StringUtils.isBlank(activeScript))
                    activeScript = MailManager.SIEVE_WEBTOP_SCRIPT;

                AutoResponder autoResp = mailManager.getAutoResponder();
                MailFiltersType type = EnumUtils.forSerializedName(id, MailFiltersType.class);
                List<MailFilter> filters = mailManager.getMailFilters(type);

                JsInMailFilters js = new JsInMailFilters(scriptCount, activeScript, autoResp, filters, profileTz);

                new JsonResult(js).printTo(out);

            } else if (crud.equals(Crud.UPDATE)) {
                Payload<MapItem, JsInMailFilters> pl = ServletUtils.getPayload(request, JsInMailFilters.class);

                if (EnumUtils.equals(pl.data.id, MailFiltersType.INCOMING)) {
                    List<MailFilter> filters = JsInMailFilters.createMailFilterList(pl.data);
                    mailManager.updateAutoResponder(JsInMailFilters.createAutoResponder(pl.data, profileTz));
                    mailManager.updateMailFilters(pl.data.id, filters);

                    boolean isWTScript = StringUtils.equals(pl.data.activeScript, MailManager.SIEVE_WEBTOP_SCRIPT);
                    if (!isWTScript && !StringUtils.isBlank(pl.data.activeScript)) {
                        try {
                            mailManager.activateSieveScript(pl.data.activeScript);
                        } catch (WTException ex1) {
                            logger.warn("Error activating chosen script", ex1);
                        }
                    }
                    // Always generate a WT script but activate it 
                    // automatically only if has been selected our script
                    mailManager.applySieveScript(isWTScript);
                }

                new JsonResult().printTo(out);
            }

        } catch (Exception ex) {
            logger.error("Error in ManageFilters", ex);
            new JsonResult(false, ex.getMessage()).printTo(out);
        }
    }

    public void processEditEmailSubject(HttpServletRequest request, HttpServletResponse response, PrintWriter out) {
        try {
            String newSubject = ServletUtils.getStringParameter(request, "subject", true);
            int messageId = ServletUtils.getIntParameter(request, "messageId", true);
            String currentFolder = ServletUtils.getStringParameter(request, "currentFolder", true);

            MailAccount account = getAccount(request);
            FolderCache folderCache = account.getFolderCache(currentFolder);
            Message currentMessage = folderCache.getMessage(messageId);

            MimeMessage newMessage = new MimeMessage((MimeMessage) currentMessage);
            newMessage.setSubject(newSubject);
            folderCache.appendMessage(newMessage);
            folderCache.deleteMessages(new long[] { messageId }, true);

            new JsonResult(true).printTo(out);

        } catch (Exception ex) {
            logger.error("Error in EditEmailSubject", ex);
            new JsonResult(ex).printTo(out);
        }
    }

    private String findActiveScriptName(List<com.fluffypeople.managesieve.SieveScript> scripts) {
        for (com.fluffypeople.managesieve.SieveScript script : scripts) {
            if (script.isActive())
                return script.getName();
        }
        return null;
    }

    private class OnUploadCloudFile implements IServiceUploadStreamListener {
        @Override
        public void onUpload(String context, HttpServletRequest request, HashMap<String, String> multipartParams,
                WebTopSession.UploadedFile file, InputStream is, MapItem responseData) throws UploadException {

            try {
                long msgid = Long.parseLong(file.getTag());
                String path = "/Emails";
                int sid = vfsmanager.getMyDocumentsStoreId();
                FileObject fo = vfsmanager.getStoreFile(sid, path);
                if (!fo.exists())
                    fo.createFolder();
                String filename = file.getFilename();
                String newFullName = vfsmanager.addStoreFileFromStream(sid, path, filename, is, false);

                filename = FilenameUtils.getName(newFullName);
                responseData.add("storeId", sid);
                responseData.add("filePath", newFullName);
                attachCloud(msgid, sid, newFullName, filename);
            } catch (UploadException ex) {
                logger.trace("Upload failure", ex);
                throw ex;
            } catch (Throwable t) {
                logger.error("Upload failure", t);
                throw new UploadException("Upload failure");
            }
        }
    }

    private static class AttachmentViewerDocumentHandler extends BaseDocEditorDocumentHandler {
        private final File file;
        private final Part part;
        private final long lastModified;

        public AttachmentViewerDocumentHandler(boolean writeCapability, UserProfileId targetProfileId,
                String documentUniqueId, Part part, long lastModified) {
            super(writeCapability, targetProfileId, documentUniqueId);
            this.part = part;
            this.lastModified = lastModified;
            this.file = null;
        }

        public AttachmentViewerDocumentHandler(boolean writeCapability, UserProfileId targetProfileId,
                String documentUniqueId, File file) {
            super(writeCapability, targetProfileId, documentUniqueId);
            this.file = file;
            this.lastModified = file.lastModified();
            this.part = null;
        }

        @Override
        public long getLastModifiedTime() throws IOException {
            return lastModified;
        }

        @Override
        public InputStream readDocument() throws IOException {
            try {
                if (part != null)
                    return part.getInputStream();
                if (file != null)
                    return new FileInputStream(file);
                throw new IOException("Unable to find part or file");
            } catch (MessagingException ex) {
                throw new IOException("Unable to read document", ex);
            }
        }

        @Override
        public void writeDocument(InputStream is) throws IOException {
            throw new IOException("Write not supported");
        }

    }

    private WebTopSession getWts() {
        return getEnv().getWebTopSession();
    }

}