/*
 * Version 1.1
 * CeCILL Copyright (c) 2006-2007, AtolCD, ADULLACT-projet
 * Initiated by AtolCD S.A. & ADULLACT-projet S.A.
 * Developped by AtolCD
 * 
 * contact@atolcd.com
 * contact@adullact-projet.coop
 * 
 * Ce logiciel est un programme informatique servant à faire circuler des 
 * documents au travers d'un circuit de validation, où chaque acteur vise 
 * le dossier, jusqu'à l'étape finale de signature.
 * 
 * Ce logiciel est régi par la licence CeCILL soumise au droit français et
 * respectant les principes de diffusion des logiciels libres. Vous pouvez
 * utiliser, modifier et/ou redistribuer ce programme sous les conditions
 * de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA 
 * sur le site "http://www.cecill.info".
 * 
 * En contrepartie de l'accessibilité au code source et des droits de copie,
 * de modification et de redistribution accordés par cette licence, il n'est
 * offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,
 * seule une responsabilité restreinte pèse sur l'auteur du programme,  le
 * titulaire des droits patrimoniaux et les concédants successifs.
 * 
 * A cet égard  l'attention de l'utilisateur est attirée sur les risques
 * associés au chargement,  à l'utilisation,  à la modification et/ou au
 * développement et à la reproduction du logiciel par l'utilisateur étant 
 * donné sa spécificité de logiciel libre, qui peut le rendre complexe à 
 * manipuler et qui le réserve donc à des développeurs et des professionnels
 * avertis possédant  des  connaissances  informatiques approfondies.  Les
 * utilisateurs sont donc invités à charger  et  tester  l'adéquation  du
 * logiciel à leurs besoins dans des conditions permettant d'assurer la
 * sécurité de leurs systèmes et ou de leurs données et, plus généralement, 
 * à l'utiliser et l'exploiter dans les mêmes conditions de sécurité. 
 * 
 * Le fait que vous puissiez accéder à cet en-tête signifie que vous avez 
 * pris connaissance de la licence CeCILL, et que vous en avez accepté les
 * termes.
 *  
 */
package com.atolcd.parapheur.repo;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.transaction.UserTransaction;

import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.filestore.FileContentWriter;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.TempFileProvider;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.contrib.ssl.EasySSLProtocolSocketFactory;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.apache.commons.ssl.KeyMaterial;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

import com.atolcd.parapheur.model.ParapheurModel;
import java.io.FileInputStream;
import java.util.Date;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.tenant.Tenant;
import org.alfresco.repo.tenant.TenantAdminService;
import org.alfresco.repo.tenant.TenantService;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.xml.sax.InputSource;

public class S2lowServiceImpl implements S2lowService, InitializingBean {

    private static Logger logger = Logger.getLogger(S2lowService.class);

    private static String S2LOW_ACTES = "/s2low/actes/*";

    private static String S2LOW_HELIOS = "/s2low/helios/*";

    private Properties configuration;

    private NodeService nodeService;

    private ContentService contentService;

    private ParapheurService parapheurService;

    private SearchService searchService;

    private NamespaceService namespaceService;

    private FileFolderService fileFolderService;

    private TransactionService transactionService;

    private AuthenticationComponent authenticationComponent;

    private TenantService tenantService;

    private TenantAdminService tenantAdminService;

    private Scheduler scheduler;

    private boolean enabled;

    public Properties getConfiguration() {
        return configuration;
    }

    /**
     * @param configuration The configuration to set.
     */
    public void setConfiguration(Properties configuration) {
        this.configuration = configuration;
    }

    /**
     * @param contentService The contentService to set.
     */
    public void setContentService(ContentService contentService) {
        this.contentService = contentService;
    }

    /**
     * @param namespaceService The namespaceService to set.
     */
    public void setNamespaceService(NamespaceService namespaceService) {
        this.namespaceService = namespaceService;
    }

    /**
     * @param fileFolderService The fileFolderService to set.
     */
    public void setFileFolderService(FileFolderService fileFolderService) {
        this.fileFolderService = fileFolderService;
    }

    /**
     * @param transactionService The transactionService to set.
     */
    public void setTransactionService(TransactionService transactionService) {
        this.transactionService = transactionService;
    }

    /**
     * @param nodeService The nodeService to set.
     */
    public void setNodeService(NodeService nodeService) {
        this.nodeService = nodeService;
    }

    /**
     * @param parapheurService The parapheurService to set.
     */
    public void setParapheurService(ParapheurService parapheurService) {
        this.parapheurService = parapheurService;
    }

    /**
     * @param searchService The searchService to set.
     */
    public void setSearchService(SearchService searchService) {
        this.searchService = searchService;
    }

    public void setAuthenticationComponent(AuthenticationComponent authenticationComponent) {
        this.authenticationComponent = authenticationComponent;
    }

    public void setTenantService(TenantService tenantService) {
        this.tenantService = tenantService;
    }

    public void setTenantAdminService(TenantAdminService tenantAdminService) {
        this.tenantAdminService = tenantAdminService;
    }

    public void setScheduler(Scheduler scheduler) {
        this.scheduler = scheduler;
    }

    /**
     * @return the enabled
     */
    public boolean isEnabled() {
        return enabled;
    }

    /**
     * @param enabled the enabled to set
     */
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    /* (non-Javadoc)
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(nodeService, "There must be a node service");
        Assert.notNull(searchService, "There must be a search service");
        Assert.notNull(contentService, "There must be a content service");
        Assert.notNull(namespaceService, "There must be a namespace service");
        Assert.notNull(parapheurService, "There must be a parapheur service");
        Assert.notNull(configuration, "There must be a configuration element");
    }

    public void restartGetS2lowStatusJobs() {
        List<Tenant> tenants = tenantAdminService.getAllTenants();

        List<String> admins = new ArrayList<String>();
        admins.add("admin"); // Execution in root tenant
        for (Tenant tenant : tenants) {
            if (tenant.isEnabled()) {
                admins.add(tenantService.getDomainUser("admin", tenant.getTenantDomain()));
            }
        }

        System.out.println(admins);
        for (String admin : admins) {
            AuthenticationUtil.runAs(new RunAsWork() {

                public Object doWork() throws Exception {
                    restartGetS2lowStatusJob();
                    return null;
                }

            }, admin);
        }


    }

    public void restartGetS2lowStatusJob() {
        UserTransaction utx = transactionService.getUserTransaction();
        try {
            utx.begin();

            String dossiersXPath = "//*[subtypeOf('ph:dossier')]";
            List<NodeRef> dossiers = searchService.selectNodes(
                    nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE),
                    dossiersXPath,
                    null,
                    namespaceService,
                    false);

            for (NodeRef dossier : dossiers) {
                EtapeCircuit etape = parapheurService.getCurrentEtapeCircuit(dossier);

                if (etape != null
                        && etape.getActionDemandee().equals(EtapeCircuit.ETAPE_TDT)
                        && nodeService.hasAspect(dossier, ParapheurModel.ASPECT_S2LOW)) {
                    startGetS2lowStatusJob(dossier);
                }
            }

            utx.commit();
        } catch (Exception e) {
            try {
                utx.rollback();
            } catch (Exception ex) {
                logger.error(ex);
            }
            throw new RuntimeException(e);
        }
    }

    public Map<String, String> getPropertiesActes() {
        try {
            Map<String, String> propertiesActes = new HashMap<String, String>();
            SAXReader reader = new SAXReader();
            Document document = reader.read(readFile("s2low_configuration.xml"));
            List<Node> nodes = document.getRootElement().element("actes").elements();
            for (Node node : nodes) {
                propertiesActes.put(node.getName(), node.getText());
            }
            return propertiesActes;
        } catch (Throwable ex) {
            logger.error(ex.getMessage(), ex);
            throw new AlfrescoRuntimeException(ex.getMessage(), ex);
        }
    }

    public Map<String, String> getPropertiesHelios() {
        try {
            Map<String, String> propertiesHelios = new HashMap<String, String>();
            SAXReader reader = new SAXReader();
            Document document = reader.read(readFile("s2low_configuration.xml"));
            List<Node> nodes = document.getRootElement().element("helios").elements();
            for (Node node : nodes) {
                propertiesHelios.put(node.getName(), node.getText());
            }
            return propertiesHelios;
        } catch (Throwable ex) {
            logger.error(ex.getMessage(), ex);
            throw new AlfrescoRuntimeException(ex.getMessage(), ex);
        }
    }

    /**
     * @see com.atolcd.parapheur.repo.ParapheurService#getXadesSignatureProperties()
     */
    public Properties getXadesSignatureProperties() {
        Map<String, String> propertiesHelios = getPropertiesHelios();
        Properties props = new Properties();
        props.setProperty("pPolicyIdentifierID", propertiesHelios.get("pPolicyIdentifierID"));
        props.setProperty("pPolicyIdentifierDescription", propertiesHelios.get("pPolicyIdentifierDescription"));
        props.setProperty("pPolicyDigest", propertiesHelios.get("pPolicyDigest"));
        props.setProperty("pSPURI", propertiesHelios.get("pSPURI"));
        props.setProperty("pCity", propertiesHelios.get("pCity"));
        props.setProperty("pPostalCode", propertiesHelios.get("pPostalCode"));
        props.setProperty("pCountryName", propertiesHelios.get("pCountryName"));
        props.setProperty("pClaimedRole", propertiesHelios.get("pClaimedRole"));
        //  logger.debug("getXadesSignatureProperties" + props.toString());
        return props;
    }

    /**
     * @see com.atolcd.parapheur.repo.ParapheurService#getS2lowClassificationNode()
     */
    public NodeRef getS2lowActesClassificationNodeRef() {
        NodeRef res = null;

        String xpath = this.configuration.getProperty("spaces.company_home.childname") + "/" +
                this.configuration.getProperty("spaces.dictionary.childname") + "/" +
                this.configuration.getProperty("spaces.configs2low.childname");

        List<NodeRef> results = null;
        try {
            results = searchService.selectNodes(
                    nodeService.getRootNode(new StoreRef(this.configuration.getProperty("spaces.store"))),
                    xpath,
                    null,
                    namespaceService,
                    false);
            if (results != null && results.size() == 1) {
                // found what we were looking for
                logger.debug("getS2lowClassificationNodeRef: Found 1 node");
                res = results.get(0);
            }
        } catch (AccessDeniedException err) {
            // ignore and return null
            logger.debug("getS2lowClassificationNodeRef: Access denied Exception");
            return null;
        }

        return res;
    }

    /**
     * @see com.atolcd.parapheur.repo.ParapheurService#getS2lowNatures()
     */
    @SuppressWarnings("unchecked")
    public Map<Integer, String> getS2lowActesNatures() {
        Map<Integer, String> mapRes = new HashMap<Integer, String>();

        logger.debug("getS2lowActesNatures: BEGIN");
        NodeRef configRef = getS2lowActesClassificationNodeRef();
        if (configRef == null) {
            logger.debug("getS2lowActesNatures: configRef is null");
        }
        ContentReader contentreader = contentService.getReader(configRef, ContentModel.PROP_CONTENT);
        SAXReader saxreader = new SAXReader();

        try {
            InputSource is = new InputSource(contentreader.getContentInputStream());
            is.setEncoding("ISO-8859-1");
            Document document = saxreader.read(is);
            Element rootElement = document.getRootElement();
            Element natures = rootElement.element("NaturesActes");
            if (natures != null) {
                Iterator<Element> elementIterator = (Iterator<Element>) natures.elementIterator("NatureActe");
                for (Iterator<Element> i = elementIterator; i.hasNext();) {
                    Element nature = i.next();
                    mapRes.put(Integer.parseInt(nature.attribute("CodeNatureActe").getValue()),
                            nature.attribute("Libelle").getValue().toString());
                }
            }
        } catch (DocumentException e) {
            if (logger.isDebugEnabled())
                logger.debug(e.getMessage(), e);
            return null;
        }

        if (logger.isDebugEnabled())
            logger.debug("getS2lowActesNatures: number of nature Elts="+mapRes.size());
        return mapRes;
    }

    /**
     * @see com.atolcd.parapheur.repo.ParapheurService#getS2lowActesClassifications()
     */
    @SuppressWarnings("unchecked")
    public Map<String, String> getS2lowActesClassifications() {
        Map<String, String> mapRes = new HashMap<String, String>();

        NodeRef configRef = getS2lowActesClassificationNodeRef();
        if (configRef == null) {
            logger.debug("getS2lowActesClassifications: configRef is null");
        }
        ContentReader contentreader = contentService.getReader(configRef, ContentModel.PROP_CONTENT);
        SAXReader saxreader = new SAXReader();

        try {
            InputSource is = new InputSource(contentreader.getContentInputStream());
            is.setEncoding("ISO-8859-1");
            Document document = saxreader.read(is);
            Element rootElement = document.getRootElement();
            Element matieres1 = rootElement.element("Matieres");
            if (matieres1 != null) {
                for (Iterator<Element> i1 = matieres1.elementIterator("Matiere1"); i1.hasNext();) {
                    Element matiere1 = i1.next();
                    String id1 = matiere1.attribute("CodeMatiere").getValue().toString();
                    mapRes.put(id1, matiere1.attribute("Libelle").getValue().toString());

                    for (Iterator<Element> i2 = matiere1.elementIterator("Matiere2"); i2.hasNext();) {
                        Element matiere2 = i2.next();
                        String id2 = id1 + "-" + matiere2.attribute("CodeMatiere").getValue().toString();
                        mapRes.put(id2, matiere2.attribute("Libelle").getValue().toString());

                        for (Iterator<Element> i3 = matiere2.elementIterator("Matiere3"); i3.hasNext();) {
                            Element matiere3 = i3.next();
                            String id3 = id2 + "-" + matiere3.attribute("CodeMatiere").getValue().toString();
                            mapRes.put(id3, matiere3.attribute("Libelle").getValue().toString());

                            for (Iterator<Element> i4 = matiere3.elementIterator("Matiere4"); i4.hasNext();) {
                                Element matiere4 = i4.next();
                                String id4 = id3 + "-" + matiere4.attribute("CodeMatiere").getValue().toString();
                                mapRes.put(id4, matiere4.attribute("Libelle").getValue().toString());

                                for (Iterator<Element> i5 = matiere4.elementIterator("Matiere5"); i5.hasNext();) {
                                    Element matiere5 = i5.next();
                                    String id5 = id4 + "-" + matiere5.attribute("CodeMatiere").getValue().toString();
                                    mapRes.put(id5, matiere5.attribute("Libelle").getValue().toString());
                                }
                            }
                        }
                    }
                }
            }
        } catch (DocumentException e) {
            return null;
        }

        return mapRes;
    }

    public void updateS2lowActesClassifications() {
        Map<String, String> propertiesActes = getPropertiesActes();

        String serverAddress = propertiesActes.get("server");
        String port = propertiesActes.get("port");

        try {
            EasySSLProtocolSocketFactory easy = new EasySSLProtocolSocketFactory();
            KeyMaterial km = new KeyMaterial(readFile(propertiesActes.get("name")), propertiesActes.get("password").toCharArray());

            easy.setKeyMaterial(km);
            Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory) easy, Integer.parseInt(port));
            Protocol.registerProtocol("https", easyhttps);
        } catch (Throwable e) {
            logger.warn("Erreur lors de la modification du protocole https");
            logger.warn(e.getMessage(), e);
        }

        HttpClient client = new HttpClient();
        GetMethod get = new GetMethod("https://" + serverAddress + ":" + port + "/modules/actes/actes_classification_fetch.php?api=1");

        try {
            int result = client.executeMethod(get);

            if (result != HttpStatus.SC_OK) {
                throw new IOException("Unexpected return code: " + result);
            }

            NodeRef contentRef = getS2lowActesClassificationNodeRef();
            ContentWriter contentWriter = contentService.getWriter(contentRef, ContentModel.PROP_CONTENT, true);

            String classification = get.getResponseBodyAsString();

            if (classification.startsWith("KO\n")) {
                throw new IOException("Unexpected error returned from server: " + classification.substring(3));
            }

            contentWriter.putContent(classification);

            logger.info("Actes classifications updated");
        } catch (IOException e) {
            logger.error("Error while fetching actes classifications: " + e.getMessage(), e);
        }
    }

    /**
     * @see com.atolcd.parapheur.repo.ParapheurService#setS2lowArchiveURL(org.alfresco.service.cmr.repository.NodeRef)
     */
    public String setS2lowActesArchiveURL(NodeRef archive) throws IOException {
        Map<String, String> propertiesActes = getPropertiesActes();
        Assert.isTrue(this.nodeService.hasAspect(archive, ParapheurModel.ASPECT_S2LOW), "Le dossier n'a pas été envoyé à la plate-forme S2LOW");
        String res = null;

        String serverAddress = propertiesActes.get("server");
        String port = propertiesActes.get("port");
        String baseURL = propertiesActes.get("baseUrlArchivage");

        String trsId = this.nodeService.getProperty(archive, ParapheurModel.PROP_TRANSACTION_ID).toString();

        if (baseURL == null) {
            baseURL = "http://localhost:8080/parapheur/";
            logger.debug("---- /!\\Impossible de prendre le parametre de config: s2low.baseUrlArchivage");
        } else {
            logger.debug("------ /!\\ baseUrlArchivage configuration OK -------");
        }
        logger.debug("setS2lowArchiveURL: trsId=" + trsId);

        // Initialisation des objets HttpClient
        // COMMONS-SSL
        try {
            EasySSLProtocolSocketFactory easy = new EasySSLProtocolSocketFactory();
            KeyMaterial km = new KeyMaterial(readFile(propertiesActes.get("name")), propertiesActes.get("password").toCharArray());

            easy.setKeyMaterial(km);
            Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory) easy, Integer.parseInt(port));
            Protocol.registerProtocol("https", easyhttps);
        } catch (Exception e) {
            logger.warn("Erreur lors de la modification du protocole https");
            logger.warn(e.getMessage(), e);
        } catch (Throwable e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // HTTPCLIENT script /modules/actes/actes_transac_set_archive_url.php
        HttpClient client = new HttpClient();
        PostMethod post = new PostMethod("https://" + serverAddress + ":" + port + "/modules/actes/actes_transac_set_archive_url.php");

        // On ne peut renseigner l'archive que si la transaction existe
        // POST id + url + api=1  , retour OK, KO

        String urlArchive = baseURL + "wcs/parapheur/archives/s2low.html?trsId=" + trsId;
        logger.debug("setS2lowArchiveURL: baseURL=[" + baseURL + "], URL1=" + urlArchive);

        // Remplissage des paramètres POST
        ArrayList<Part> parts = new ArrayList<Part>();
        parts.add(new StringPart("id", trsId));
        parts.add(new StringPart("url", urlArchive));
        parts.add(new StringPart("api", "1"));

        Part[] partsTab = new Part[parts.size()];
        for (int i = 0; i < parts.size(); i++) {
            partsTab[i] = parts.get(i);
        }

//      partsTab[0] = new StringPart("id", trsId );
//      partsTab[1] = new StringPart("url", urlArchive);
//      partsTab[2] = new StringPart("api", "1");

        //ExternalAccessServlet.resolveNamePath(sc, path)
        //urlArchive = ExternalAccessServlet.generateExternalURL(ExternalAccessServlet.OUTCOME_BROWSE, archive.getStoreRef()+"/stv/"+archive.getId());
        //logger.debug("setS2lowArchiveURL: URL2="+ urlArchive);

        post.setRequestEntity(new MultipartRequestEntity(partsTab, post.getParams()));
        // Exécution de la méthode POST
        try {
            logger.debug("=== Before executeMethod");
            int status = client.executeMethod(post);
            logger.debug("=== After  executeMethod");
            if (HttpStatus.SC_OK == status) {
                String reponse = post.getResponseBodyAsString();
                String[] tab = reponse.split("\n");
                if (tab.length == 1 || "KO".equals(tab[0])) {
                    StringBuilder errorBuilder = new StringBuilder("Erreur retournée par la plate-forme s2low : ");
                    for (int i = 1; i < tab.length; i++) {
                        errorBuilder.append(tab[i]);
                    }
                    throw new RuntimeException(errorBuilder.toString());
                }
                // La transaction s'est bien passée, on sort gentiment
                logger.debug("C'est OK!");
                res = urlArchive;
            } else {
                throw new IOException("Echec de la récupération de la connexion à la plate-forme : statut = " + status);
            }
        } finally {
            post.releaseConnection();
        }

        // ça semble assez débile et hasardeux de se reposer sur le client Web
        // TODO  --> étudier le webscript, plus simple; mais attention à l'authentification (wcs?)
        return res;
    }

    /**
     * @see com.atolcd.parapheur.repo.ParapheurService#getInfosS2low(org.alfresco.service.cmr.repository.NodeRef)
     */
    public int getInfosS2low(NodeRef dossier) throws IOException {
        Assert.isTrue(this.parapheurService.isDossier(dossier), "Node Ref doit être de type ph:dossier");
        Assert.isTrue(this.nodeService.hasAspect(dossier, ParapheurModel.ASPECT_S2LOW), 
                "Le dossier "+nodeService.getProperty(dossier, ContentModel.PROP_NAME)+" n'a pas été envoyé à la plate-forme S2LOW");

        int result;

        if ("HELIOS".equals(this.nodeService.getProperty(dossier, ParapheurModel.PROP_TDT_PROTOCOLE))) {
            result = getInfosS2lowHelios(dossier);
        } else {
            result = getInfosS2lowActes(dossier);
        }


        if (!(result == 1 || result == 2 || result == 3 || result == 7) &&
                (parapheurService.getCurrentEtapeCircuit(dossier).getActionDemandee().equals(EtapeCircuit.ETAPE_TDT) ||
                parapheurService.getCurrentEtapeCircuit(dossier).getActionDemandee().equals(EtapeCircuit.ETAPE_ARCHIVAGE))) {
            if (result == -1 || result == 6) { // Erreur ou NACK
                // Pour ce cas TRES particulier on audite le transmissionKO
                parapheurService.auditTransmissionTDT(dossier, "TransmissionKO", "Réponse TDT: " + statutS2lowToString(result));
                parapheurService.reject(dossier);
            } else { // C'est OK: on continue le circuit
                if (!parapheurService.getCurrentEtapeCircuit(dossier).getActionDemandee().equals(EtapeCircuit.ETAPE_ARCHIVAGE)) {
                    parapheurService.approve(dossier);
                }
            }
            try {
                scheduler.unscheduleJob(
                        (String) nodeService.getProperty(dossier, ContentModel.PROP_NAME),
                        "S2LOW");
            } catch (SchedulerException ex) {
                logger.error("Exception canceling Job", ex);
            }
        }

        return result;
    }

    private int getInfosS2lowActes(NodeRef dossier) throws IOException {
        Map<String, String> propertiesActes = getPropertiesActes();
        String res = null;
        String serverAddress = propertiesActes.get("server");
        String port = propertiesActes.get("port");
        int codeRetour = -1;

        // Initialisation des objets HttpClient
        // COMMONS-SSL
        try {
            EasySSLProtocolSocketFactory easy = new EasySSLProtocolSocketFactory();
            KeyMaterial km = new KeyMaterial(readFile(propertiesActes.get("name")), propertiesActes.get("password").toCharArray());

            easy.setKeyMaterial(km);
            Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory) easy, Integer.parseInt(port));
            Protocol.registerProtocol("https", easyhttps);
        } catch (Exception e) {
            logger.warn("Erreur lors de la modification du protocole https");
            logger.warn(e.getMessage(), e);
        } catch (Throwable e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // HTTPCLIENT
        HttpClient client = new HttpClient();
        GetMethod get = new GetMethod("https://" + serverAddress + ":" + port +
                "/modules/actes/actes_transac_get_status.php?transaction=" +
                this.nodeService.getProperty(dossier, ParapheurModel.PROP_TRANSACTION_ID));

        // Exécution de la méthode GET
        try {
            int status = client.executeMethod(get);
            if (HttpStatus.SC_OK == status) {
                String reponse = get.getResponseBodyAsString();
                String[] tab = reponse.split("\n");
                if (tab.length == 1 || "KO".equals(tab[0])) {
                    StringBuilder error = new StringBuilder("Erreur retournée par la plate-forme s2low : ");
                    for (int i = 1; i < tab.length; i++) {
                        error.append(tab[i]);
                    }
                    throw new RuntimeException(error.toString());
                }

                // La méthode a changé en 1.0.5, logger ce qu'on a...
                for (int i = 0; i < tab.length; i++) {
                    logger.debug("Réponse Statut S2LOW: [" + tab[i] + "]");
                }

                // La  transaction s'est bien passée, on renvoie le statut (ligne 2)
                codeRetour = Integer.parseInt(tab[1]);
                res = statutS2lowToString(codeRetour);
                // On enregistre le statut renvoyé
                this.nodeService.setProperty(dossier, ParapheurModel.PROP_STATUS, res);

                // Avec S2LOW 1.0.5 ACTES: Dans les cas 3,4 et 6, le status est accompagné du message de retour envoyé par la préfecture
                // Seuls les cas 4-Ack et 6-Nack sont pertinents ici
                if (tab.length > 2 && (codeRetour == 4 || codeRetour == 6)) {
                    String ARActeXml = "";
                    for (int i = 2; i < tab.length; i++) {
                        ARActeXml += tab[i] + "\n";
                    }
                    // On enregistre dans PROP_ARACTE_XML
                    ContentWriter arWriter = this.contentService.getWriter(dossier, ParapheurModel.PROP_ARACTE_XML, true);
                    arWriter.setMimetype(MimetypeMap.MIMETYPE_XML);
                    arWriter.setEncoding("UTF-8");  // FIXME: c'est peut-etre ISO-8859-1
                    arWriter.putContent(ARActeXml);
                }
            } else {
                throw new RuntimeException("Echec de la récupération de la connexion à la plate-forme : statut = " + status);
            }
        } finally {
            get.releaseConnection();
        }

        return codeRetour;
    }

    private int getInfosS2lowHelios(NodeRef dossier) throws IOException {
        Map<String, String> propertiesHelios = getPropertiesHelios();
        String serverAddress = propertiesHelios.get("server");
        String port = propertiesHelios.get("port");
        int codeRetour = -1;

        // Initialisation des objets HttpClient
        // COMMONS-SSL
        try {
            EasySSLProtocolSocketFactory easy = new EasySSLProtocolSocketFactory();
            KeyMaterial km = new KeyMaterial(readFile(propertiesHelios.get("name")), propertiesHelios.get("password").toCharArray());

            easy.setKeyMaterial(km);
            Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory) easy, Integer.parseInt(port));
            Protocol.registerProtocol("https", easyhttps);
        } catch (Exception e) {
            logger.warn("Erreur lors de la modification du protocole https");
            logger.warn(e.getMessage(), e);
        } catch (Throwable e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // HTTPCLIENT
        HttpClient client = new HttpClient();
        GetMethod get = new GetMethod("https://" + serverAddress + ":" + port +
                "/modules/helios/api/helios_transac_get_status.php?transaction=" +
                this.nodeService.getProperty(dossier, ParapheurModel.PROP_TRANSACTION_ID));

        // Exécution de la méthode GET
        try {
            int status = client.executeMethod(get);
            if (HttpStatus.SC_OK == status) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Reponse s2low=[" + get.getResponseBodyAsString() + "]");
                }
                InputStream reponse = get.getResponseBodyAsStream();
                //            <transaction>
                //              <id> numéro de la transaction  </id>
                //              <resultat> OK ou KO </resultat>
                //              <status> status </status>
                //              <message> message complémentaire </message>
                //            </transaction>
                SAXReader saxreader = new SAXReader();
                try {
                    Document document = saxreader.read(reponse);
                    Element rootElement = document.getRootElement();
                    Element xid = rootElement.element("id");//.getTextTrim();
                    Element xresultat = rootElement.element("resultat");
                    Element xstatus = rootElement.element("status");
                    Element xmessage = rootElement.element("message");
                    if (xid == null || xresultat == null || xstatus == null || "KO".equals(xresultat.getTextTrim())) {
                        String error = "Erreur retournée par la plate-forme s2low : ";
                        if (xmessage != null) {
                            error += xmessage.getTextTrim();
                        }
                        throw new RuntimeException(error);
                    }
                    // La  transaction s'est bien passée, on enregistre le statut
                    codeRetour = Integer.parseInt(xstatus.getTextTrim());
                    this.nodeService.setProperty(dossier, ParapheurModel.PROP_STATUS, statutS2lowToString(codeRetour));

                    // Si NACK, téléchargement de l'acquitement.
                    if (codeRetour == 6) {
                        downloadS2lowHeliosNAck(dossier);
                    }
                } catch (DocumentException e) {
                    throw new RuntimeException("Echec de la récupération de la réponse du TdT", e);
                }
            } else {
                throw new RuntimeException("Echec de la récupération de la connexion à la plate-forme : statut = " + status);
            }
        } finally {
            get.releaseConnection();
        }

        return codeRetour;
    }

    private void downloadS2lowHeliosNAck(NodeRef dossier) throws IOException {
        Map<String, String> propertiesHelios = getPropertiesHelios();
        String serverAddress = propertiesHelios.get("server");
        String port = propertiesHelios.get("port");

        // Initialisation des objets HttpClient
        // COMMONS-SSL
        try {
            EasySSLProtocolSocketFactory easy = new EasySSLProtocolSocketFactory();
            KeyMaterial km = new KeyMaterial(readFile(propertiesHelios.get("name")), propertiesHelios.get("password").toCharArray());

            easy.setKeyMaterial(km);
            Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory) easy, Integer.parseInt(port));
            Protocol.registerProtocol("https", easyhttps);
        } catch (Exception e) {
            logger.warn("Erreur lors de la modification du protocole https");
            logger.warn(e.getMessage(), e);
        } catch (Throwable e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // HTTPCLIENT
        HttpClient client = new HttpClient();
        GetMethod get = new GetMethod("https://" + serverAddress + ":" + port +
                "/modules/helios/helios_download_acquit.php?id=" +
                this.nodeService.getProperty(dossier, ParapheurModel.PROP_TRANSACTION_ID));

        try {
            int status = client.executeMethod(get);
            if (HttpStatus.SC_OK == status) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Reponse s2low=[" + get.getResponseBodyAsString() + "]");
                }
                InputStream reponse = get.getResponseBodyAsStream();

                ContentWriter writer = contentService.getWriter(dossier, ParapheurModel.PROP_NACKHELIOS_XML, true);
                writer.setMimetype(MimetypeMap.MIMETYPE_XML);
                writer.putContent(reponse);
            }
        } finally {
            get.releaseConnection();
        }
    }

    private InputStream readFile(String nameFile) throws Throwable {
        UserTransaction trx = transactionService.getUserTransaction();
        InputStream inputStream = null;
        try {
            trx.begin();

            // Recherche du noeud Certificats dans Dictionary
            String xpath = "/app:company_home/app:dictionary/ph:certificats";
            ResultSet result = searchService.query(new StoreRef(this.configuration.getProperty("spaces.store")), searchService.LANGUAGE_XPATH, xpath);

            if (result.length() > 0) {
                NodeRef nodeCertificat = result.getNodeRef(0);
                List<ChildAssociationRef> childs = nodeService.getChildAssocs(nodeCertificat);
                NodeRef file = null;

                // Récupération des fichiers concernant le certificat
                for (ChildAssociationRef child : childs) {
                    String name = nodeService.getProperty(child.getChildRef(), ContentModel.PROP_NAME).toString();
                    if (name.equals(nameFile)) {
                        // Sélection du fichier demandé
                        file = child.getChildRef();
                    }
                }

                ContentReader content = fileFolderService.getReader(file);
                inputStream = content.getContentInputStream();
            }
            trx.commit();
            return inputStream;
        } catch (Throwable e) {
            try {
                trx.rollback();
            } catch (Exception ex) {
                logger.error("Error during rollback", ex);
            }
            throw e;
        }
    }

    /**
     * @see com.atolcd.parapheur.repo.ParapheurService#statutS2lowToString(int)
     */
    public String statutS2lowToString(int code) {
        String res = null;
        switch (code) {
            case -1:
                res = "Erreur";
                break;
            case 0:
                res = "Annulé";
                break;
            case 1:
                res = "Posté";
                break;
            case 2:
                res = "En attente de transmission";
                break;
            case 3:
                res = "Transmis";
                break;
            case 4:
                res = "Acquittement reçu";
                break;
            case 5:
                res = "Validé";
                break;
            case 6:
                res = "Refusé";
                break;
            case 7:
                res = "En traitement";
                break;
        }
        return res;
    }

    /**
     * @see com.atolcd.parapheur.repo.ParapheurService#envoiS2low(org.alfresco.service.cmr.repository.NodeRef, java.lang.String)
     */
    public void envoiS2lowActes(NodeRef dossier, String nature, String classification, String numero, String objet, String date) throws IOException {
        Assert.isTrue(this.parapheurService.isDossier(dossier), "Node Ref doit être de type ph:dossier");
        Assert.isTrue(!this.nodeService.hasAspect(dossier, ParapheurModel.ASPECT_S2LOW), "Le dossier a déjà été envoyé à la plate-forme S2LOW");
        Map<String, String> propertiesActes = getPropertiesActes();

        String serverAddress = propertiesActes.get("server");
        String port = propertiesActes.get("port");

        // Initialisation des objets HttpClient
        // COMMONS-SSL
        try {
            EasySSLProtocolSocketFactory easy = new EasySSLProtocolSocketFactory();
            KeyMaterial km = new KeyMaterial(readFile(propertiesActes.get("name")), propertiesActes.get("password").toCharArray());

            easy.setKeyMaterial(km);
            Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory) easy, Integer.parseInt(port));
            Protocol.registerProtocol("https", easyhttps);
        } catch (Exception e) {
            logger.warn("Erreur lors de la modification du protocole https");
            logger.warn(e.getMessage(), e);
        } catch (Throwable e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // HTTPCLIENT
        HttpClient client = new HttpClient();
        PostMethod post = new PostMethod("https://" + serverAddress + ":" + port + "/modules/actes/actes_transac_create.php");

        // Récupération des fichiers
        List<NodeRef> documents = this.parapheurService.getDocuments(dossier);
        ArrayList<FilePart> fileParts = new ArrayList<FilePart>();
        // Premier fichier: Fichier PDF de l'ACTE
        ContentReader mainDocReader = this.contentService.getReader(documents.get(0), ContentModel.PROP_CONTENT);
        logger.debug("envoiS2low (" + documents.size() + " docs): ajout mainDoc type " + mainDocReader.getMimetype());
        File ficPdf = ensureMimeType(mainDocReader, MimetypeMap.MIMETYPE_PDF);
        fileParts.add(new FilePart("acte_pdf_file", ficPdf));
        // Signature Electronique de l'ACTE
        if (MimetypeMap.MIMETYPE_PDF.equals(mainDocReader.getMimetype())) {
            List<EtapeCircuit> etapes = this.parapheurService.getCircuit(dossier);
            if (etapes.get(0).getActionDemandee() == null) {   // compatibilité ascendante
                byte[] signature = this.parapheurService.getSignature(dossier);
                if (signature != null && signature.length > 0) {
                    logger.debug("envoiS2low: (oldschool) signature PDF presente");
                    File ficSign = TempFileProvider.createTempFile("s2low", "p7s");
                    FileOutputStream os = null;
                    try {
                        os = new FileOutputStream(ficSign);
                        os.write(signature);
                    } finally {
                        if (os != null) {
                            os.close();
                        }
                    }
                    fileParts.add(new FilePart("acte_pdf_file_sign", ficSign));
                }
            } else {  // on n'envoie QUE la dernière signature.
                EtapeCircuit lastSigEtape = null;
                for (EtapeCircuit etape : etapes) {
                    if (etape.getActionDemandee().trim().equalsIgnoreCase(EtapeCircuit.ETAPE_SIGNATURE) && this.parapheurService.getSignature(etape) != null) {
                        lastSigEtape = etape;
                    }
                }
                if (lastSigEtape != null) {
                    byte[] signature = this.parapheurService.getSignature(lastSigEtape);
                    if (signature != null && signature.length > 0) {
                        logger.debug("envoiS2low: signature PDF presente");
                        File ficSign = TempFileProvider.createTempFile("s2low", "p7s");
                        FileOutputStream os = new FileOutputStream(ficSign);
                        os.write(signature);
                        os.close();
                        fileParts.add(new FilePart("acte_pdf_file_sign", ficSign));
                    }
                }
            }
        }

        // Suivants
        // TODO Pour l'instant les PJ sont envoyées sans signature
        for (int i = 1; i < documents.size(); i++) {
            NodeRef doc = documents.get(i);
            // Correction bug #1869 pièces attachées ne sont pas converties: autorisé PDF JPG PNG
            ContentReader attReader = this.contentService.getReader(doc, ContentModel.PROP_CONTENT);
            if (MimetypeMap.MIMETYPE_IMAGE_JPEG.equalsIgnoreCase(attReader.getMimetype()) || MimetypeMap.MIMETYPE_IMAGE_PNG.equalsIgnoreCase(attReader.getMimetype())) {
                // authorized formats JPG/PNG: no need for PDF conversion
                logger.debug("envoiS2low (doc num " + i + "): ajout piece jointe img: " + attReader.getMimetype());
                File ficPdfx = TempFileProvider.createTempFile("s2low-Att" + i, null);
                attReader.getContent(ficPdfx);
                fileParts.add(new FilePart("acte_attachments[]", ficPdfx));
            } else {
                // convert to PDF if needed
                logger.debug("envoiS2low (doc num " + i + "): ajout piece jointe type " + attReader.getMimetype());
                File ficPdfx = ensureMimeType(attReader, MimetypeMap.MIMETYPE_PDF);
                fileParts.add(new FilePart("acte_attachments[]", ficPdfx));
            }
//         File ficPdfx = TempFileProvider.createTempFile("s2low-Att"+i, attReader.getMimetype());
//         attReader.getContent(ficPdfx);
//         fileParts.add(new FilePart("acte_attachments[]", ficPdfx));
        }

        // Lecture de la classification
        ArrayList<StringPart> classifParts = new ArrayList<StringPart>();
        String[] classifications = classification.split("-");
        for (int i = 1; i <= classifications.length; i++) {
            classifParts.add(new StringPart("classif" + i, classifications[i - 1]));
        }

        // Remplissage des paramètres POST
        ArrayList<Part> parts = new ArrayList<Part>();
        parts.add(new StringPart("api", "1"));
        parts.add(new StringPart("nature_code", nature));
        for (StringPart classif : classifParts) {
            parts.add(classif);
        }
        parts.add(new StringPart("number", new String(numero.getBytes(), "ISO-8859-1")));
        parts.add(new StringPart("decision_date", new String(date.getBytes(), "ISO-8859-1")));

        if (logger.isDebugEnabled()) {
            // FIXME l'encodage de l'objet est pourri!!
            logger.debug("envoiS2low: number [" + numero + "], [" + new String(numero.getBytes(), "ISO-8859-1") + "]");
            logger.debug("envoiS2low: decision_date [" + date + "], [" + new String(date.getBytes(), "ISO-8859-1") + "]");
            logger.debug("envoiS2low: objet [" + objet + "], [" + new String(objet.getBytes(), "ISO-8859-1") + "]");
            if (Charset.isSupported("ISO-8859-1")) {
                logger.debug("  Default charset: " + Charset.defaultCharset() + ", ISO-8859-1 is supported");
            }
            logger.debug("   Request Charset: " + post.getRequestCharSet());
        }

        parts.add(new StringPart("subject", new String(objet.getBytes("UTF-8"))));
        for (FilePart ficPart : fileParts) {
            parts.add(ficPart);
        }
        Part[] partsTab = new Part[parts.size()];
        for (int i = 0; i < parts.size(); i++) {
            partsTab[i] = parts.get(i);
        }

        post.setRequestEntity(new MultipartRequestEntity(partsTab, post.getParams()));
        // Exécution de la méthode POST
        try {
            // logger.debug("QueryString=[[" + post.toString() + "]]");
            int status = client.executeMethod(post);
            if (HttpStatus.SC_OK == status) {
                String reponse = post.getResponseBodyAsString();
                String[] tab = reponse.split("\n");
                if (tab.length == 1 || "KO".equals(tab[0])) {
                    StringBuilder error = new StringBuilder("Erreur retournée par la plate-forme s2low : ");
                    for (int i = 1; i < tab.length; i++) {
                        error.append(tab[i]);
                    }
                    throw new RuntimeException(error.toString());
                }

                // La  transaction s'est bien passée, on renvoie son identifiant
                int res = Integer.parseInt(tab[1]);
                // On enregistre le numéro de transaction attribué par la plate-forme
                Map<QName, Serializable> pptes = new HashMap<QName, Serializable>();
                pptes.put(ParapheurModel.PROP_TRANSACTION_ID, res);
                this.nodeService.addAspect(dossier, ParapheurModel.ASPECT_S2LOW, pptes);
                parapheurService.auditTransmissionTDT(dossier, "EnCoursTransmission", "Dossier en télétransmission");

                startGetS2lowStatusJob(dossier);
            } else {
                logger.error("Echec envoi S2LOW: statut=" + status + ", " + HttpStatus.getStatusText(status));
                throw new RuntimeException("Echec de la récupération de la connexion à la plate-forme : statut = " +
                        status + ", " + HttpStatus.getStatusText(status));
            }
        } finally {
            post.releaseConnection();
        }
    }

    /**
     * @see com.atolcd.parapheur.repo.S2lowService#envoiS2lowHelios(org.alfresco.service.cmr.repository.NodeRef)
     */
    public void envoiS2lowHelios(NodeRef dossier) throws IOException {
        Assert.isTrue(this.parapheurService.isDossier(dossier), "Node Ref doit être de type ph:dossier");
        Assert.isTrue(!this.nodeService.hasAspect(dossier, ParapheurModel.ASPECT_S2LOW), "Le dossier a déjà été envoyé à la plate-forme S2LOW");
        Map<String, String> propertiesHelios = getPropertiesHelios();

        String serverAddress = propertiesHelios.get("server");
        String port = propertiesHelios.get("port");

        // Initialisation des objets HttpClient
        // COMMONS-SSL
        try {
            EasySSLProtocolSocketFactory easy = new EasySSLProtocolSocketFactory();
            KeyMaterial km = new KeyMaterial(readFile(propertiesHelios.get("name")), propertiesHelios.get("password").toCharArray());

            easy.setKeyMaterial(km);
            Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory) easy, Integer.parseInt(port));
            Protocol.registerProtocol("https", easyhttps);
        } catch (Exception e) {
            logger.error("Erreur lors de la modification du protocole https");
            if (logger.isDebugEnabled()) {
                logger.debug(e.getMessage(), e);
            }
            throw new RuntimeException("Erreur sur parametrage https");
        } catch (Throwable e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            throw new RuntimeException("Erreur sur parametrage https");
        }

        // HTTPCLIENT
        HttpClient client = new HttpClient();
        // API S2LOW-Helios v2.0.1 du 9/9/2009
        PostMethod post = new PostMethod("https://" + serverAddress + ":" + port + "/modules/helios/api/helios_importer_fichier.php");

        // Récupération des fichiers
        List<NodeRef> documents = this.parapheurService.getDocuments(dossier);
        ArrayList<FilePart> fileParts = new ArrayList<FilePart>();
        ArrayList<Part> parts = new ArrayList<Part>();

        // Premier fichier; Cette version d'API ne permet l'envoi que d'un fichier XML (le premier doc du dossier)
        ContentReader mainDocReader = this.contentService.getReader(documents.get(0), ContentModel.PROP_CONTENT);
        String mainDocMimeType = mainDocReader.getMimetype().trim();
        if (logger.isDebugEnabled()) {
            logger.debug("envoiS2lowHelios (" + documents.size() + " docs): ajout mainDoc type " + mainDocMimeType);
        }
        if (!mainDocMimeType.equalsIgnoreCase(MimetypeMap.MIMETYPE_XML) &&
                !mainDocMimeType.contains("text/xml") &&
                !mainDocMimeType.contains("application/xml") &&
                !mainDocMimeType.contains("application/readerpesv2") &&
                !mainDocMimeType.equalsIgnoreCase("application/vnd.xemelios-xml")) {
            throw new RuntimeException("Echec d'envoi: La plateforme HELIOS attend un fichier XML.");
        }
        File ficXml = ensureMimeType(mainDocReader, mainDocMimeType);
        fileParts.add(new FilePart("enveloppe", ficXml));

        for (FilePart ficPart : fileParts) {
            parts.add(ficPart);
        }
        Part[] partsTab = new Part[parts.size()];
        for (int i = 0; i < parts.size(); i++) {
            partsTab[i] = parts.get(i);
        }

        if (logger.isDebugEnabled()) {
            logger.debug("   Request Charset: " + post.getRequestCharSet());
        }
        post.setRequestEntity(new MultipartRequestEntity(partsTab, post.getParams()));
        // Exécution de la méthode POST
        try {
            // logger.debug("QueryString=[[" + post.toString() + "]]");
            int status = client.executeMethod(post);
            if (HttpStatus.SC_OK == status) {
                String sreponse = post.getResponseBodyAsString();
                if (logger.isDebugEnabled()) {
                    logger.debug("Reponse s2low=[" + sreponse + "]");
                }
                if (sreponse.startsWith("KO")) {
                    String error = "Erreur retournée par la plate-forme s2low : ";
                    throw new RuntimeException(error + sreponse.replace('\n', ':'));
                }
                InputStream reponse = post.getResponseBodyAsStream();
                // Le retour est un message formaté XML
                //           <import>
                //             <id> numéro de la transaction créée </id>
                //             <resultat> OK ou KO </resultat>
                //             <message> message complémentaire </message>
                //           </import>
                SAXReader saxreader = new SAXReader();
                try {
                    Document document = saxreader.read(reponse);
                    Element rootElement = document.getRootElement();
                    Element id = rootElement.element("id");//.getTextTrim();
                    Element resultat = rootElement.element("resultat");
                    Element message = rootElement.element("message");
                    if (id == null || resultat == null || "KO".equals(resultat.getTextTrim())) {
                        String error = "Erreur retournée par la plate-forme s2low : ";
                        if (message != null) {
                            error += message.getTextTrim();
                        }
                        throw new RuntimeException(error);
                    }
                    // La  transaction s'est bien passée, on enregistre le numéro de transaction
                    int res = Integer.parseInt(id.getTextTrim());
                    Map<QName, Serializable> pptes = new HashMap<QName, Serializable>();
                    pptes.put(ParapheurModel.PROP_TRANSACTION_ID, res);
                    this.nodeService.addAspect(dossier, ParapheurModel.ASPECT_S2LOW, pptes);
                    parapheurService.auditTransmissionTDT(dossier, "EnCoursTransmission", "Dossier en télétransmission");

                    startGetS2lowStatusJob(dossier);
                } catch (DocumentException e) {
                    throw new RuntimeException("Echec de la récupération de la réponse du TdT");
                }
            } else {
                throw new RuntimeException("Echec de la récupération de la connexion à la plate-forme : statut = " + status);
            }
        } finally {
            post.releaseConnection();
        }
    }

    protected void startGetS2lowStatusJob(NodeRef dossierRef) {
        try {
            // Trigger: Execute in 2 minutes, and repeat every 5 minutes
            Trigger trigger = new SimpleTrigger(
                    (String) nodeService.getProperty(dossierRef, ContentModel.PROP_NAME),
                    "S2LOW",
                    new Date(System.currentTimeMillis() + (2 * 60 * 1000)), // Now + 2 minutes
                    null,
                    SimpleTrigger.REPEAT_INDEFINITELY,
                    5 * 60 * 1000); // 5 minutes

            JobDetail details = new JobDetail(
                    (String) nodeService.getProperty(dossierRef, ContentModel.PROP_NAME),
                    "S2LOW",
                    S2lowGetInfosJob.class);

            Map<String, Object> jobDataMap = new HashMap<String, Object>();
            jobDataMap.put("s2lowService", this);
            jobDataMap.put("transactionService", transactionService);
            jobDataMap.put("dossier", dossierRef);
            jobDataMap.put("runAs", parapheurService.getActeurCourant(dossierRef));
            details.setJobDataMap(new JobDataMap(jobDataMap));

            scheduler.scheduleJob(details, trigger);
        } catch (Exception e) {
            throw new RuntimeException("Error scheduling job", e);
        }
    }

    /**
     * @see com.atolcd.parapheur.repo.S2lowService#getS2lowHeliosListePES_Retour()
     */
    @SuppressWarnings("unchecked")
    public void getS2lowHeliosListePES_Retour() throws IOException {
        Map<String, String> propertiesHelios = getPropertiesHelios();
        // get liste des ids de pes_retour selon numéro de collectivité

        if (this.isEnabled()) {

            String serverAddress = propertiesHelios.get("server");
            String port = propertiesHelios.get("port");
            String idCollectivite = propertiesHelios.get("collectivite");
            String nomParapheur = propertiesHelios.get("parapheur");
            logger.debug("getS2lowHeliosListePES_Retour Debut");

            // Initialisation des objets HttpClient
            // COMMONS-SSL
            try {
                EasySSLProtocolSocketFactory easy = new EasySSLProtocolSocketFactory();
                KeyMaterial km = new KeyMaterial(readFile(propertiesHelios.get("name")), propertiesHelios.get("password").toCharArray());

                easy.setKeyMaterial(km);
                Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory) easy, Integer.parseInt(port));
                Protocol.registerProtocol("https", easyhttps);
            } catch (Exception e) {
                //logger.warn(e.getStackTrace());
                throw new IOException("getS2lowHeliosListePES_Retour: HTTPS impossible.");
            } catch (Throwable e) {
                // TODO Auto-generated catch block
                throw new IOException("getS2lowHeliosListePES_Retour: Erreur sur modification https (catch throwable)");
                // e.printStackTrace();
            }

            // HTTPCLIENT
            HttpClient client = new HttpClient();
            GetMethod get = new GetMethod("https://" + serverAddress + ":" + port +
                    "/modules/helios/api/helios_get_list.php?collectivite=" + idCollectivite);

            // Exécution de la méthode GET
            try {
                logger.debug("Debut: try");
                int status = client.executeMethod(get);
                logger.debug("After helios_get_list.php?collectivite=" + idCollectivite);
                if (HttpStatus.SC_OK == status) {
                    String sreponse = get.getResponseBodyAsString();
                    if (logger.isDebugEnabled()) {
                        logger.debug("Reponse s2low=[" + sreponse + "]");
                    }
                    if (sreponse.startsWith("error")) {
                        throw new RuntimeException("Erreur retournée par la plate-forme s2low: " + sreponse);
                    } else if (sreponse.trim().isEmpty()) {
                        throw new RuntimeException("Erreur: S2LOW renvoie une chaine VIDE !");
                    }
                    InputStream reponse = get.getResponseBodyAsStream();
                    //     <liste>
                    //             <idColl> identifiant de la collectivité </idColl>
                    //             <dateDemande> date de demande de la liste </dateDemande>
                    //             <resultat> OK ou KO </resultat>
                    //             <message> message complémentaire </message>
                    //             <pes_retour>
                    //                 <id> identifiant du pes_retour  </id>
                    //                 <nom> nom </nom>
                    //                 <date> date de réception du pes_retour par le tdt </date>
                    //             </pes_retour>
                    //             <pes_retour>
                    //                 <id> identifiant du pes_retour  </id>
                    //                 <nom> nom </nom>
                    //                 <date> date de réception du pes_retour par le tdt </date>
                    //             </pes_retour>
                    //             ...
                    //     </liste>

                    SAXReader saxreader = new SAXReader();
                    try {
                        Document document = saxreader.read(reponse);
                        Element rootElement = document.getRootElement();
                        Element xresultat = rootElement.element("resultat");
                        Element xmessage = rootElement.element("message");
                        if (xresultat == null || "KO".equals(xresultat.getTextTrim())) {
                            String error = "Erreur retournée par la plate-forme s2low : ";
                            if (xmessage != null) {
                                error += xmessage.getTextTrim();
                            }
                            logger.error(error);
                            throw new RuntimeException(error);
                        }
                        // La  transaction s'est bien passée, on traite la liste
                        Iterator<Element> elementIterator = (Iterator<Element>) rootElement.elementIterator("pes_retour");
                        for (Iterator<Element> i = elementIterator; i.hasNext();) {
                            Element pes_retour = i.next();
                            Element eltIdPes = pes_retour.element("id");
                            Element eltNom = pes_retour.element("nom");
                            if (eltIdPes == null) {
                                throw new DocumentException("Traitement liste pes_retour: Element 'id' attendu, mais absent.");
                            }
                            if (eltNom == null) {
                                throw new DocumentException("Traitement liste pes_retour: Element 'nom' attendu, mais absent.");
                            }
                            if (logger.isDebugEnabled()) {
                                logger.debug("Trouvé: id=" + eltIdPes + ", nom=" + eltNom + ", date=" + pes_retour.element("date"));
                            }
                            // TODO
                            // 1- récup. PES_Retour
                            String idPes = eltIdPes.getTextTrim();
                            InputStream xmlPesRetour = getS2lowHeliosPES_Retour(idPes);
                            // 2- créer dossier PES.    WARN: dans quel parapheur? info à choper dans fichier conf.: nomParapheur
                            NodeRef dossierCree = null;
                            if (!parapheurService.isNomDossierAlreadyExists(eltNom.getTextTrim())) {
                                dossierCree = this.parapheurService.createDossierPesRetour(eltNom.getTextTrim(), nomParapheur, xmlPesRetour);
                            }
                            // 3- si ok: changer status à "lu" sur s2low: helios_change_status.php
                            if (null != dossierCree) {
                                setS2lowHeliosChangeStatus(idPes);
                            } else {
                                logger.error(idCollectivite + ": Dossier '"+ eltNom.getTextTrim() +"' non cree dans parapheur '"+ nomParapheur +"', verifier configuration.");
                            }

                        }

                    } catch (DocumentException e) {
                        throw new RuntimeException("Echec de la récupération de la réponse du TdT", e);
                    }
                } else {
                    throw new RuntimeException("Echec de la récupération de la connexion à la plate-forme : statut = " + status);
                }
            } finally {
                get.releaseConnection();
            }
        } // if (this.isEnabled())
    }

    private InputStream getS2lowHeliosPES_Retour(String idRetour) throws IOException {
        Map<String, String> propertiesHelios = getPropertiesHelios();
        // helios_get_retour
        //   Assert.isInstanceOf(Integer.TYPE, Integer.parseInt(idRetour), "l'identifiant doit etre un entier");
        String serverAddress = propertiesHelios.get("server");
        String port = propertiesHelios.get("port");

        // Initialisation des objets HttpClient
        // COMMONS-SSL
        try {
            EasySSLProtocolSocketFactory easy = new EasySSLProtocolSocketFactory();
            KeyMaterial km = new KeyMaterial(readFile(propertiesHelios.get("name")), propertiesHelios.get("password").toCharArray());

            easy.setKeyMaterial(km);
            Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory) easy, Integer.parseInt(port));
            Protocol.registerProtocol("https", easyhttps);
        } catch (Exception e) {
            logger.warn("Erreur lors de la modification du protocole https");
            logger.warn(e.getMessage(), e);
        } catch (Throwable e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // HTTPCLIENT
        HttpClient client = new HttpClient();
        GetMethod get = new GetMethod("https://" + serverAddress + ":" + port +
                "/modules/helios/api/helios_get_retour.php?idRetour=" + idRetour);

        // Exécution de la méthode GET
        InputStream res = null;
        try {
            int status = client.executeMethod(get);
            if (HttpStatus.SC_OK == status) {
                res = get.getResponseBodyAsStream();
            } else {
                throw new RuntimeException("Echec de la récupération de la connexion à la plate-forme : statut = " + status);
            }
            // Copy result to temporary file.
            File tempFile = TempFileProvider.createTempFile("helios-" + idRetour, ".xml");
            FileOutputStream out = null;
            try {
                out = new FileOutputStream(tempFile);
                byte[] buffer = new byte[4096];
                int readed;
                while ((readed = res.read(buffer)) >= 0) {
                    out.write(buffer, 0, readed);
                }
                out.flush();
            } finally {
                if (out != null) {
                    out.close();
                }
            }

            res = new FileInputStream(tempFile);
        } finally {
            get.releaseConnection();
        }
        return res;
    }

    private void setS2lowHeliosChangeStatus(String idRetour) throws IOException {
        Map<String, String> propertiesHelios = getPropertiesHelios();
        //  helios_change_status.php
        String serverAddress = propertiesHelios.get("server");
        String port = propertiesHelios.get("port");

        // Initialisation des objets HttpClient
        // COMMONS-SSL
        try {
            EasySSLProtocolSocketFactory easy = new EasySSLProtocolSocketFactory();
            KeyMaterial km = new KeyMaterial(readFile(propertiesHelios.get("name")), propertiesHelios.get("password").toCharArray());

            easy.setKeyMaterial(km);
            Protocol easyhttps = new Protocol("https", (ProtocolSocketFactory) easy, Integer.parseInt(port));
            Protocol.registerProtocol("https", easyhttps);
        } catch (Exception e) {
            logger.warn("Erreur lors de la modification du protocole https");
            logger.warn(e.getMessage(), e);
        } catch (Throwable e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // HTTPCLIENT
        HttpClient client = new HttpClient();
        GetMethod get = new GetMethod("https://" + serverAddress + ":" + port +
                "/modules/helios/api/helios_change_status.php?idRetour=" + idRetour);

        // Exécution de la méthode GET
        InputStream reponse = null;
        try {
            int status = client.executeMethod(get);
            if (HttpStatus.SC_OK == status) {
                reponse = get.getResponseBodyAsStream();
                if (logger.isDebugEnabled()) {
                    logger.debug("Reponse s2low=[" + get.getResponseBodyAsString().toString() + "]");
                }
                //            <change>
                //              <id> numéro de la transaction  </id>
                //              <resultat> OK ou KO </resultat>
                //              <message> message complémentaire </message>
                //            </change>
                SAXReader saxreader = new SAXReader();
                try {
                    Document document = saxreader.read(reponse);
                    Element rootElement = document.getRootElement();
                    Element xresultat = rootElement.element("resultat");
                    Element xmessage = rootElement.element("message");
                    if (xresultat == null || "KO".equals(xresultat.getTextTrim())) {
                        String error = "Erreur retournée par la plate-forme s2low : ";
                        if (xmessage != null) {
                            error += xmessage.getTextTrim();
                        }
                        throw new RuntimeException(error);
                    }
                } catch (DocumentException e) {
                    throw new RuntimeException("Echec de la récupération de la réponse du TdT");
                }
            } else {
                throw new RuntimeException("Echec de la récupération de la connexion à la plate-forme : statut = " + status);
            }
        } finally {
            get.releaseConnection();
        }
    }

    private File ensureMimeType(ContentReader reader, String mimeType) {
        File fic = TempFileProvider.createTempFile("s2low", null);
        // Si le type MIME correspond, on l'envoie tel quel
        if (mimeType.equals(reader.getMimetype())) {
            reader.getContent(fic);
        } // Sinon, on commence par le transformer
        else {
            FileContentWriter tmpWriter = new FileContentWriter(fic);
            tmpWriter.setMimetype(mimeType);
            tmpWriter.setEncoding(reader.getEncoding());
            this.contentService.transform(reader, tmpWriter);
        }

        return fic;
    }

}
