xtremweb.dispatcher.HTTPHandler.java Source code

Java tutorial

Introduction

Here is the source code for xtremweb.dispatcher.HTTPHandler.java

Source

/*
 * Copyrights     : CNRS
 * Author         : Oleg Lodygensky
 * Acknowledgment : XtremWeb-HEP is based on XtremWeb 1.8.0 by inria : http://www.xtremweb.net/
 * Web            : http://www.xtremweb-hep.org
 *
 *      This file is part of XtremWeb-HEP.
 *
 *    XtremWeb-HEP is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    XtremWeb-HEP 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 General Public License
 *    along with XtremWeb-HEP.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package xtremweb.dispatcher;

import java.io.ByteArrayInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.URISyntaxException;
import java.nio.channels.DatagramChannel;
import java.nio.charset.Charset;
import java.rmi.RemoteException;
import java.security.AccessControlException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import javax.net.ssl.SSLSocket;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;

import com.auth0.jwt.interfaces.DecodedJWT;

import xtremweb.common.BytePacket;
import xtremweb.common.DataInterface;
import xtremweb.common.DataTypeEnum;
import xtremweb.common.Logger;
import xtremweb.common.StatusEnum;
import xtremweb.common.StreamIO;
import xtremweb.common.Table;
import xtremweb.common.UID;
import xtremweb.common.UserInterface;
import xtremweb.common.UserRightEnum;
import xtremweb.common.XMLHashtable;
import xtremweb.common.XMLable;
import xtremweb.common.XWConfigurator;
import xtremweb.common.XWPropertyDefs;
import xtremweb.common.XWTools;
import xtremweb.communications.Connection;
import xtremweb.communications.IdRpc;
import xtremweb.communications.URI;
import xtremweb.communications.XMLRPCCommand;
import xtremweb.communications.XMLRPCCommandActivateHost;
import xtremweb.communications.XMLRPCCommandChmod;
import xtremweb.communications.XMLRPCCommandWorkAlive;
import xtremweb.communications.XWPostParams;
import xtremweb.database.SQLRequest;
import xtremweb.dispatcher.HTTPOAuthHandler.OAuthException;
import xtremweb.security.XWAccessRights;

/**
 * This handles incoming communications through TCP<br>
 * This answers request from TCPClient
 *
 * Created: August 2005
 *
 * @see xtremweb.communications.TCPClient
 * @author Oleg Lodygensky
 * @version RPCXW
 */

public class HTTPHandler extends xtremweb.dispatcher.CommHandler {

    public static final String PATH = "/";

    public static final String APIPATH = "/api";
    /**
     * This is the name of the cookie containing the user UID
     */
    public static final String COOKIE_USERUID = "USERUID";
    /**
     * This is the name of the cookie containing the facebook app id
     */
    public static final String COOKIE_FACEBOOKAPP = "XWFACEBOOKAPP";
    /**
     * This is the name of the cookie containing the google app id
     */
    public static final String COOKIE_GOOGLEAPP = "XWGOOGLEAPP";
    /**
     * This is the name of the cookie containing the twitter app id
     */
    public static final String COOKIE_TWITTERAPP = "XWTWITTERAPP";
    /**
     * This is the name of the cookie containing the yahoo app id
     */
    public static final String COOKIE_YAHOOAPP = "XWYAHOOAPP";

    /**
     * This is the resource path in the xtremweb java archive (xtremweb.jar) :
     * "data"
     */
    public static final String RESOURCE_PATH = "misc";
    /**
     * This is the Bootstrap dashboard HTML file name : dashboard.html
     *
     * @since 10.2.0
     */
    public static final String DASHBOARDFILENAME_HTML = "/dashboard.html";
    /**
     * This is the Bootstrap dashboard CSS file name : dashboard.css
     *
     * @since 10.2.0
     */
    public static final String DASHBOARDFILENAME_CSS = "/dashboard.css";
    /**
     * This is JavaScript -extracted from xwserver.html- file name : xwserver.js
     *
     * @since 10.2.0
     */
    public static final String SCRIPTFILENAME_JS = "/xwserver.js";
    /**
     * This is the favicon file name : favicon.ico
     *
     * @since 10.2.0
     */
    public static final String FAVICOFILENAME_ICO = "/favicon.ico";
    /**
     * This is the logo file name : logo.jpg
     */
    public static final String RESOURCEFILENAME_LOGO = "/logo.jpg";

    private static final String[] names = { DASHBOARDFILENAME_HTML, DASHBOARDFILENAME_CSS, FAVICOFILENAME_ICO,
            RESOURCEFILENAME_LOGO, SCRIPTFILENAME_JS };
    private static final String TEXTHTML = "text/html";
    private static final String TEXTCSS = "text/css";
    private static final String IMAGEXICON = "image/x-icon";
    private static final String IMAGEJPEG = "image/jpeg";
    private static final String APPJS = "application/javascript";
    private static final String[] mimeTypes = { TEXTHTML, TEXTCSS, IMAGEXICON, IMAGEJPEG, APPJS };

    /**
     * This enumerates resources needed by the dashboard html file (css, images,
     * javascript)
     *
     * @since 8.3.0
     */
    public enum Resources {
        /**
         * This is the Bootstrap dashboard HTML
         *
         * @since 10.2.0
         */
        DASHBOARDHTML,
        /**
         * This is the Bootstrap carousel HTML
         *
         * @since 10.2.0
         */
        DASHBOARDCSS,
        /**
         * This is the favicon resource
         *
         * @since 10.2.0
         */
        FAVICON, LOGO,
        /**
         * This is JavaScript, extracted from xwserver.html
         *
         * @since 10.2.0
         */
        XWJS;

        /**
         * This retrieves the resource name started with a slash. This is both
         * used to retrieve resource from the archive and as path for web access
         *
         * @return the resource name
         */
        public String getName() {
            return names[this.ordinal()];
        }

        /**
         * This retrieves the resource path in the xtremweb java archive
         * (xtremweb.jar)
         *
         * @return the resource path in the archive
         */
        public String getPath() {
            return RESOURCE_PATH + this.getName();
        }

        /**
         * @return the resource path
         */
        public String getMimeType() {
            return mimeTypes[this.ordinal()];
        }

        /**
         * This writes resource content to the given response. This calls
         * writeBinary() for non text resources
         *
         * @param response
         *            is the output channel to write to
         * @throws IOException
         *             is thrown if resource is not found
         */
        public void write(final HttpServletResponse response) throws IOException {
            final InputStream reader = getClass().getClassLoader().getResourceAsStream(getPath());
            if (reader == null) {
                throw new IOException(getPath() + " not found");
            }

            response.setHeader(CONTENTLENGTHLABEL, Integer.toString(reader.available()));

            final OutputStream out = response.getOutputStream();
            final byte[] buf = new byte[10240];
            for (int n = reader.read(buf); n > 0; n = reader.read(buf)) {
                out.write(buf, 0, n);
            }
            out.flush();
        }

    }

    /** This is the content type label HTTP header */
    private static final String CONTENTTYPELABEL = "Content-Type";
    /** This is the content type default value : application/octet-stream */
    private static final String CONTENTTYPEVALUE = "application/octet-stream";
    /** This is the content length label HTTP header */
    private static final String CONTENTLENGTHLABEL = "Content-Length";
    /** This is the content shasum label HTTP header */
    private static final String CONTENTSHASUMLABEL = "Content-SHASUM";
    /** This is the content disposition label HTTP header */
    private static final String CONTENTDISPOSITIONLABEL = "Content-Disposition";
    /** This is the last modified label HTTP header */
    private static final String LASTMODIFIEDLABEL = "Last-Modified";

    private static final String DEFAULT_ANSWER_HEAD = "<html><head><title>XtremWeb-HEP API</title></head>"
            + "<body><center><h1>XtremWeb-HEP API</h1><br /><h3>%s</h3></center><br /><br /><a href=\"/\">Go to the client interface</a><br /><br />"
            + "Available interface commands :<br /><ul>";
    private static final String DEFAULT_UPLOAD_FORM = "<form action=\"%s/" + IdRpc.UPLOADDATA + "\""
            + " enctype=\"multipart/form-data\" method=\"post\">"
            + "<div style=\"margin:10px;padding:10px;background-color:lightgrey\"><p><h2>Upload form</h2></p>"
            + "<p style=\"color:red\">You must have registered your data first</p>" + "<p>Registered Data UID:<br>"
            + "<input type=\"text\" name=\"" + XWPostParams.DATAUID + "\" size=\"40\"></p><p>"
            + "<p>Registered Data size:<br>" + "<input type=\"text\" name=\"" + XWPostParams.DATASIZE
            + "\" size=\"40\"></p><p>" + "<p>Registered Data shasum:<br>" + "<input type=\"text\" name=\""
            + XWPostParams.DATASHASUM + "\" size=\"40\"></p><p>" + "Please specify a file:<br>"
            + "<input type=\"file\" name=\"" + XWPostParams.DATAFILE + "\" size=\"45\"></p><div>"
            + "<input type=\"submit\" value=\"Send\"></div></form>";

    private static final String DEFAULT_ANSWER_TAIL = "</ul><br /><ul><li> to retrieve a specific objet : %s/get/a_specific_uid</ul></body></html>";

    private IdRpc idRpc;

    private synchronized void resetIdRpc() {
        getLogger().debug(Thread.currentThread().getId() + " : resetIdRpc");
        idRpc = null;
        notifyAll();
    }

    private synchronized void setIdRpc(final IdRpc i) {
        getLogger().debug(Thread.currentThread().getId() + " : setIdRpc " + i);
        idRpc = i;
        notifyAll();
    }

    private synchronized void waitIdRpc(final IdRpc i) {
        while (getIdRpc() != null) {
            try {
                getLogger().debug(Thread.currentThread().getId() + " : waitIdRpc " + i);
                wait();
                getLogger().debug(Thread.currentThread().getId() + " : waitIdRpc woken up");
            } catch (final InterruptedException e) {
            }
        }
        setIdRpc(i);
        notifyAll();
    }

    private synchronized IdRpc getIdRpc() {
        getLogger().warn(Thread.currentThread().getId() + " : getIdRpc a ete synchronise ! " + idRpc);
        getLogger().debug(Thread.currentThread().getId() + " : getIdRpc " + idRpc);
        return idRpc;
    }

    protected HttpServletRequest request;
    protected HttpServletResponse response;
    private FileItem dataUpload;
    private String dataUploadshasum;
    private final FileItemFactory diskFactory;
    private final ServletFileUpload servletUpload;

    public static final String NAME = "HTTPHandler";
    /**
     * This is the email address header in the certificate distinguish name.
     * <br />
     * The certificate distinguish name looks like
     * <code>EMAILADDRESS=lodygens@lal.in2p3.fr, CN=Oleg Lodygensky, OU=UMR8607, O=CNRS, C=FR</code>
     *
     * @since 8.3.0
     */
    private static final String DNHEADER_EMAIL = "EMAILADDRESS=";
    private static final int DNHEADERLENGTH_EMAIL = DNHEADER_EMAIL.length();
    /**
     * This is the common name header in the certificate distinguish name.<br />
     *
     * @see #DNHEADER_EMAIL
     * @since 8.3.0
     */
    private static final String DNHEADER_CN = "CN=";
    private static final int DNHEADERLENGTH_CN = DNHEADER_CN.length();
    /**
     * This is the organization unit header in the certificate distinguish name.
     * <br />
     *
     * @see #DNHEADER_EMAIL
     * @since 8.3.0
     */
    private static final String DNHEADER_OU = "OU=";
    private static final int DNHEADERLENGTH_OU = DNHEADER_OU.length();
    /**
     * This is the organization header in the certificate distinguish name.
     * <br />
     *
     * @see #DNHEADER_EMAIL
     * @since 8.3.0
     */
    private static final String DNHEADER_O = "O=";
    private static final int DNHEADERLENGTH_O = DNHEADER_O.length();
    /**
     * This is the country header in the certificate distinguish name.<br />
     *
     * @see #DNHEADER_EMAIL
     * @since 8.3.0
     */
    private static final String DNHEADER_C = "C=";
    private static final int DNHEADERLENGTH_C = DNHEADER_C.length();

    /**
     * This is the default constructor which only calls super(NAME)
     */
    public HTTPHandler() {
        super(NAME);
        dataUpload = null;
        diskFactory = new DiskFileItemFactory();
        servletUpload = new ServletFileUpload(diskFactory);
        servletUpload.setSizeMax(XWPostParams.MAXUPLOADSIZE);
        getLogger().debug("new Thread " + Thread.currentThread().getName());
    }

    /**
     * This is the default constructor which only calls super(NAME)
     */
    public HTTPHandler(final XWConfigurator c) {
        this(NAME, c);
    }

    /**
     *
     */
    public HTTPHandler(final String n, final XWConfigurator c) {
        super(n, c);
        dataUpload = null;
        diskFactory = new DiskFileItemFactory();
        servletUpload = new ServletFileUpload(diskFactory);
        servletUpload.setSizeMax(XWPostParams.MAXUPLOADSIZE);
        getLogger().debug("new Thread " + Thread.currentThread().getName());
    }

    /**
     * This constructor call the previous constructor
     *
     * @param socket
     *            is not used
     * @see #HTTPHandler(XWConfigurator)
     */
    public HTTPHandler(final SSLSocket socket, final XWConfigurator c) throws RemoteException {
        this(c);
    }

    /**
     * This does nothing
     */
    @Override
    public void setSocket(final Socket s) throws RemoteException {
    }

    /**
     * This does nothing
     */
    @Override
    public void setSocket(final SSLSocket s) throws RemoteException {
    }

    /**
     * This throws an exception since setPacket() is dedicated to UDP comms
     *
     * @exception RemoteException
     *                is always thrown since this method is dedicated to UDP
     *                comms
     */
    @Override
    public void setPacket(final DatagramSocket s, final DatagramPacket p) throws RemoteException {
        throw new RemoteException("HTTPHandler#setPacket() TCP can't set packet");
    }

    /**
     * @see xtremweb.communications.CommHandler#setSocket(Socket)
     * @exception RemoteException
     *                is always thrown since this method is dedicated to UDP
     *                comms
     */
    @Override
    public void setPacket(final DatagramChannel c, final SocketAddress r, final BytePacket p)
            throws RemoteException {
        throw new RemoteException("HTTPHandler#setPacket() TCP can't set packet");
    }

    /**
     * This does nothing and must be overidden by any HTTP handler This is
     * inherited from org.mortbay.jetty.Handler
     */
    @Override
    public void setServer(final Server server) {
    }

    /**
     * This does nothing and must be overidden by any HTTP handler This is
     * inherited from org.mortbay.jetty.Handler
     */
    @Override
    public Server getServer() {
        return null;
    }

    /**
     * This does nothing and must be overidden by any HTTP handler This is
     * inherited from org.mortbay.component.LifeCycle
     *
     * @return true
     */
    @Override
    public boolean isFailed() {
        return true;
    }

    /**
     * This does nothing and must be overidden by any HTTP handler This is
     * inherited from org.mortbay.component.LifeCycle
     *
     * @return false
     */
    @Override
    public boolean isRunning() {
        return false;
    }

    /**
     * This does nothing and must be overidden by any HTTP handler This is
     * inherited from org.mortbay.component.LifeCycle
     *
     * @return false
     */
    @Override
    public boolean isStarted() {
        return false;
    }

    /**
     * This does nothing and must be overidden by any HTTP handler This is
     * inherited from org.mortbay.component.LifeCycle
     *
     * @return false
     */
    @Override
    public boolean isStarting() {
        return false;
    }

    /**
     * This does nothing and must be overidden by any HTTP handler This is
     * inherited from org.mortbay.component.LifeCycle
     *
     * @return true
     */
    @Override
    public boolean isStopped() {
        return true;
    }

    /**
     * This does nothing and must be overidden by any HTTP handler This is
     * inherited from org.mortbay.component.LifeCycle
     *
     * @return false
     */
    @Override
    public boolean isStopping() {
        return false;
    }

    /**
     * This does nothing and must be overidden by any HTTP handler This is
     * inherited from org.mortbay.component.LifeCycle
     */
    @Override
    public void start() {
    }

    /**
     * This writes parameter to output channel
     */
    @Override
    protected synchronized void write(final XMLable answer) throws IOException {

        final Logger logger = getLogger();
        mileStone("<write>");

        if (getIdRpc() == IdRpc.DOWNLOADDATA) {
            logger.debug("DOWNLOADDATA : don't write out");
            response.setStatus(HttpServletResponse.SC_OK);
            resetIdRpc();
            notifyAll();
            return;
        }

        if (response == null) {
            mileStone("<error method='write' msg='no response' />");
            mileStone("</write>");
            getLogger().error("Can't write : this.response is not set");
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            resetIdRpc();
            notifyAll();
            return;
        }

        try {
            response.setDateHeader("Date", new Date().getTime());
            response.setContentType("text/xml;charset=UTF-8");
            String msg = XMLable.XMLHEADER + "<" + XMLable.ROOTTAG + ">";
            // response.getWriter().println(XMLable.XMLHEADER + "<" +
            // XMLable.ROOTTAG + ">");
            if (answer != null) {
                getLogger().debug("HTTPHandler#write(" + answer.toXml() + ")");
                // response.getWriter().println(answer.toXml());
                msg += answer.toXml();
            }
            msg += "</" + XMLable.ROOTTAG + ">";
            response.setHeader(CONTENTLENGTHLABEL, Integer.toString(msg.length()));
            response.getWriter().println(msg);
        } catch (final Exception e) {
            logger.exception(e);
            mileStone("<error method='write' msg='" + e.getMessage() + "' />");
            throw new IOException(e.toString());
        } finally {
            response.setStatus(HttpServletResponse.SC_OK);
            resetIdRpc();
            mileStone("</write>");
            notifyAll();
        }
    }

    /**
     * This sets google application cookie
     *
     * @since 10.5.0
     */
    private void setFacebookAppCookie() {
        if (HTTPOAuthHandler.Operator.FACEBOOK.getAppId() == null) {
            getLogger().debug("Facebook AppId not set");
            return;
        }
        final String facebookAppID = Dispatcher.getConfig().getProperty(XWPropertyDefs.FACEBOOKAPPID);
        final boolean facebookApp = (facebookAppID != null) && (facebookAppID.length() > 0);
        if (facebookApp) {
            final Cookie cookiefacebook = new Cookie(COOKIE_FACEBOOKAPP, facebookAppID);
            getLogger().debug(COOKIE_FACEBOOKAPP + " = " + facebookAppID);
            response.addCookie(cookiefacebook);
        }
    }

    /**
     * This sets google application cookie
     *
     * @since 10.5.0
     */
    private void setGoogleAppCookie() {
        if (HTTPOAuthHandler.Operator.GOOGLE.getAppId() == null) {
            getLogger().debug("Google AppId not set");
            return;
        }
        final String googleAppID = Dispatcher.getConfig().getProperty(XWPropertyDefs.GOOGLEAPPID);
        getLogger().debug("googleAppID = " + googleAppID);
        final boolean googleApp = (googleAppID != null) && (googleAppID.length() > 0);
        if (googleApp) {
            final Cookie cookieGoogle = new Cookie(COOKIE_GOOGLEAPP, googleAppID);
            getLogger().debug(COOKIE_GOOGLEAPP + " = " + googleAppID);
            response.addCookie(cookieGoogle);
        }
    }

    /**
     * This sets twitter application cookie
     *
     * @since 10.5.0
     */
    private void setTwitterAppCookie() {
        if (HTTPOAuthHandler.Operator.TWITTER.getAppId() == null) {
            getLogger().debug("Twitter AppId not set");
            return;
        }
        final String twitterAppID = Dispatcher.getConfig().getProperty(XWPropertyDefs.TWITTERAPPID);
        final boolean twitterApp = (twitterAppID != null) && (twitterAppID.length() > 0);
        if (twitterApp) {
            final Cookie cookieTwitter = new Cookie(COOKIE_TWITTERAPP, twitterAppID);
            getLogger().debug(COOKIE_TWITTERAPP + " = " + twitterAppID);
            response.addCookie(cookieTwitter);
        }
    }

    /**
     * This sets yahoo application cookie
     *
     * @since 10.5.0
     */
    private void setYahooAppCookie() {
        if (HTTPOAuthHandler.Operator.YAHOO.getAppId() == null) {
            getLogger().debug("Yahoo AppId not set");
            return;
        }
        final String yahooAppID = Dispatcher.getConfig().getProperty(XWPropertyDefs.YAHOOAPPID);
        final boolean yahooApp = (yahooAppID != null) && (yahooAppID.length() > 0);
        if (yahooApp) {
            final Cookie cookieYahoo = new Cookie(COOKIE_YAHOOAPP, yahooAppID);
            getLogger().debug(COOKIE_YAHOOAPP + " = " + yahooAppID);
            response.addCookie(cookieYahoo);
        }
    }

    /**
     * This sets application cookie
     *
     * @since 10.5.0
     */
    private void setAppCookies() {
        setFacebookAppCookie();
        setGoogleAppCookie();
        setTwitterAppCookie();
        setYahooAppCookie();
    }

    /**
     * This sends the HTML page to the client
     *
     * @since 8.1.0
     */
    private void sendDashboard(final UserInterface client) throws IOException {

        final Cookie cookieUser = new Cookie(COOKIE_USERUID, client.getUID().toString());
        getLogger().debug("setCookie " + COOKIE_USERUID + " = " + client.getUID().toString());
        response.addCookie(cookieUser);
        setAppCookies();
        sendResource(Resources.DASHBOARDHTML);
    }

    /**
     * This sends the resource to the client
     *
     * @since 8.3.0
     */
    private void sendResource(final Resources r) throws IOException {

        getLogger()
                .debug("sendind " + r + " ; " + (r.getMimeType() != null ? r.getMimeType() : "unkown mime type"));
        if (r.getMimeType() != null) {
            response.setContentType(r.getMimeType());
        }
        r.write(response);
        //      response.getWriter().flush();
        response.setStatus(HttpServletResponse.SC_OK);
    }

    /**
     * This displays the page proposing to log using openid or to go to the
     * statistics page This sends back the HTML status
     * HttpServletResponse.SC_UNAUTHORIZED
     *
     * @since 8.2.0
     */
    private void redirectPage(final Request baseRequest, final Resources res) throws IOException {
        getLogger().debug("redirectPage " + res);
        setAppCookies();
        response.setContentType(TEXTHTML);
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        response.sendRedirect(res.getName());
        baseRequest.setHandled(true);
    }

    /**
     * This writes the API to output stream
     *
     * @since 8.0.2
     */
    protected synchronized void writeApi(final UserInterface client, final URI baseUri) throws IOException {

        if (response == null) {
            throw new IOException("Can't write : this.response is not set");
        }

        mileStone("<writeApi str='" + baseUri + "'>");

        try {
            final PrintWriter writer = response.getWriter();
            final StringBuilder msg = new StringBuilder();
            if (client != null) {
                msg.append(String.format(DEFAULT_ANSWER_HEAD,
                        "You are logged as \"" + client.getEMail() != null ? client.getEMail() : client.getLogin())
                        + "\"");
            } else {
                msg.append(String.format(DEFAULT_ANSWER_HEAD, "<i>You are not logged</i>"));
            }
            response.setContentType(TEXTHTML + ";charset=UTF-8");

            for (final IdRpc i : IdRpc.values()) {
                msg.append("<li> <a href=\"" + baseUri + "/" + i + "\">" + baseUri + "/" + i + "</a> : "
                        + i.helpRestApi());
            }

            msg.append(String.format(DEFAULT_UPLOAD_FORM, baseUri));
            msg.append(String.format(DEFAULT_ANSWER_TAIL, baseUri));

            response.setHeader(CONTENTLENGTHLABEL, "" + msg.length());
            writer.println(msg);
            response.setStatus(HttpServletResponse.SC_OK);
        } catch (final Exception e) {
            getLogger().exception(e);
            mileStone("<error method='writeapi' msg='" + e.getMessage() + "' />");
            throw new IOException(e.toString());
        } finally {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            resetIdRpc();
            mileStone("</writeApi>");
            notifyAll();
        }
    }

    /**
     * This writes parameter to output channel
     */
    protected synchronized <T extends Table> void writeRows(final T row) throws IOException {

        if (response == null) {
            throw new IOException("Can't write : this.response is not set");
        }
        mileStone("<writeRows>");

        try {
            final Collection<T> rows = DBInterface.getInstance().selectAllPublic(row);

            if (rows != null) {
                final Iterator<T> rowsEnum = rows.iterator();

                while (rowsEnum.hasNext()) {
                    final T r = rowsEnum.next();
                    if (r instanceof UserInterface) {
                        ((UserInterface) r).setCertificate("****");
                        ((UserInterface) r).setPassword("****");
                    }
                    response.getWriter().println(r.toXml());
                }
            }
        } catch (final Exception e) {
            getLogger().exception(e);
            mileStone("<error method='writeRows' msg='" + e.getMessage() + "' />");
            getLogger().error(e.toString());
            throw new IOException(e.toString());
        } finally {
            response.setStatus(HttpServletResponse.SC_OK);
            mileStone("</writeRows>");
            notifyAll();
        }
    }

    /**
     * This write file content to output stream
     *
     * @param f
     *            is the file to write
     */
    @Override
    public synchronized void writeFile(final File f) throws IOException {
        try (final StreamIO io = new StreamIO(new DataOutputStream(response.getOutputStream()), null, false)) {
            mileStone("<writeFile file='" + f + "'>");
            io.writeFileContent(f, XWPostParams.MAXUPLOADSIZE);
        } catch (final Exception e) {
            getLogger().exception(e);
            mileStone("<error method='writeFile' msg='" + e.getMessage() + "' />");
            throw new IOException(e.toString());
        } finally {
            mileStone("</writeFile>");
            notifyAll();
        }
    }

    /**
     * This is not implemented and always throws an IOException
     */
    @Override
    public void readFile(final File f) throws IOException {
        throw new IOException("HTTPHandler#readFile not implemented");
    }

    private UserInterface getUser() throws IOException {
        UserInterface user = null;
        user = userFromBasicAuth(request);
        if (user != null) {
            return user;
        }
        try {
            user = userFromJWTEthereumAuth(request);
            if (user != null) {
                return user;
            }
            user = userFromOAuth(request);
            if (user != null) {
                return user;
            }
            user = userFromOpenId(request);
            if (user != null) {
                return user;
            }
            user = userFromPostParams(request);
            if (user != null) {
                return user;
            }
            user = userFromCertificate(request);
            if (user != null) {
                return user;
            }
            return null;
        } catch (NoSuchAlgorithmException e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * This retrieves noredirect from query string
     * @return true by default; the value of noredirect from the current request query string
     * @since 12.2.9
     * @see XWPostParams#NOREDIRECT
     */
    protected boolean redirect() {
        try {
            final String value = request.getParameter(XWPostParams.NOREDIRECT.toString().toLowerCase()) != null
                    ? request.getParameter(XWPostParams.NOREDIRECT.toString().toLowerCase())
                    : request.getParameter(XWPostParams.NOREDIRECT.toString());

            return !Boolean.parseBoolean(value);
        } catch (final Exception e) {
            getLogger().exception(e);
            return true;
        }
    }

    /**
     * This handles incoming connections. This is inherited from
     * org.mortbay.jetty.Handler. This expects a POST parameter :
     * XWPostParams.COMMAND
     * @see xtremweb.communications.XWPostParams
     */
    @Override
    public synchronized void handle(final String target, final Request baseRequest,
            final HttpServletRequest _request, final HttpServletResponse _response)
            throws IOException, ServletException {

        XMLRPCCommand command = null;
        final Logger logger = getLogger();
        Table obj = null;
        dataUpload = null;

        request = _request;
        response = _response;
        response.setHeader("Access-Control-Allow-Origin", "*");

        String reqUri = baseRequest.getRequestURI();

        if (request.getUserPrincipal() == null) {
            logger.debug("Handling user principal = null");
        } else {
            logger.debug("Handling user principal = " + request.getUserPrincipal().getName());
        }
        logger.debug("Handling target         = " + target);
        logger.finest("Handling request        = " + request.getContentLength() + " " + request.getContentType());
        logger.debug("Handling request auth   = " + request.getAuthType());
        logger.debug("Handling request user   = " + request.getRemoteUser());
        logger.debug("Handling parameter size = " + request.getParameterMap().size());
        logger.debug("Handling query string   = " + request.getQueryString());
        logger.debug("Handling method         = " + request.getMethod());
        logger.debug("Request URI             = " + reqUri);
        // logger.debug("Request server = " + request.getServerName());
        // logger.debug("Request port = " + request.getServerPort());
        // logger.debug("Authorization = " +
        // request.getHeader(HttpHeaders.AUTHORIZATION));
        for (final Enumeration<String> e = request.getParameterNames(); e.hasMoreElements();) {
            logger.finest("parameter " + e.nextElement());
        }
        for (final Enumeration<String> e = request.getHeaderNames(); e.hasMoreElements();) {
            final String headerName = e.nextElement();
            logger.finest("header " + headerName + " : " + request.getHeader(headerName));
        }

        final Cookie[] cookies = request.getCookies();
        logger.debug("cookies.length = " + (cookies == null ? "0" : cookies.length));
        if (cookies != null) {
            for (int c = 0; c < cookies.length; c++) {
                final Cookie cookie = cookies[c];
                logger.finest("cookie " + cookie.getName() + " : " + cookie.getValue());
            }
        }
        if (request.getParameterMap().size() <= 0) {

            if (target.equals(PATH)) {
                logger.debug("redirecting to dashboard");
                redirectPage(baseRequest, Resources.DASHBOARDHTML);
                return;
            }

            for (final Resources r : Resources.values()) {
                if (r.getName().compareToIgnoreCase(target) == 0) {
                    logger.debug("redirecting to " + r);
                    sendResource(r);
                    return;
                }
            }
        }

        final HttpSession session = request.getSession(true);
        final String mandatingLogin = (request.getParameter(XWPostParams.XWMANDATINGLOGIN.toString()) != null
                ? request.getParameter(XWPostParams.XWMANDATINGLOGIN.toString())
                : (String) session.getAttribute(XWPostParams.XWMANDATINGLOGIN.toString()));
        final String authState = (request.getParameter(XWPostParams.AUTH_STATE.toString()) != null
                ? request.getParameter(XWPostParams.AUTH_STATE.toString())
                : (String) session.getAttribute(XWPostParams.AUTH_STATE.toString()));
        final String authEmail = (request.getParameter(XWPostParams.AUTH_EMAIL.toString()) != null
                ? request.getParameter(XWPostParams.AUTH_EMAIL.toString())
                : (String) session.getAttribute(XWPostParams.AUTH_EMAIL.toString()));
        final String authId = (request.getParameter(XWPostParams.AUTH_IDENTITY.toString()) != null
                ? request.getParameter(XWPostParams.AUTH_IDENTITY.toString())
                : (String) session.getAttribute(XWPostParams.AUTH_IDENTITY.toString()));

        getLogger().debug("oauthState = " + authState);
        getLogger().debug("oauthEmail= " + authEmail);
        getLogger().debug("oauthId= " + authId);

        final UserInterface user = getUser();

        final Vector<String> paths = (Vector<String>) XWTools.split(target, "/");
        logger.debug("paths.size() = " + paths.size());

        URI baseUri = null;
        try {
            baseUri = new URI(Connection.httpsScheme() + "://" + XWTools.getHostName(request.getServerName()) + ":"
                    + request.getServerPort());
        } catch (final URISyntaxException e) {
            resetIdRpc();
            throw new IOException(e);
        }

        if (target.compareToIgnoreCase(APIPATH) == 0) {
            resetIdRpc();
            writeApi(user, baseUri);
            baseRequest.setHandled(true);
            return;
        }

        if (!target.equals(PATH)) {
            try {
                final IdRpc i = IdRpc.valueOf(paths.elementAt(0).toUpperCase());
                waitIdRpc(i);
            } catch (final Exception e) {
                // let other handlers manage this (e.g. /stats, /openid)
                logger.debug("not handling " + reqUri);
                return;
            }
        }

        if (user == null) {
            resetIdRpc();
            logger.debug("no credential found");
            redirectPage(baseRequest, Resources.DASHBOARDHTML);
            return;
        }

        logger.debug("User    = " + user.toString());
        try {

            final boolean isMultipart = ServletFileUpload.isMultipartContent(request);

            logger.debug("Handling ismultipart    = " + isMultipart);

            //
            // HTML form
            //
            if (isMultipart) {
                final List parts = servletUpload.parseRequest(request);
                logger.debug("parts.size = " + parts.size());
                if (parts != null) {
                    for (final Iterator it = parts.iterator(); it.hasNext();) {
                        final FileItem item = (FileItem) it.next();

                        logger.debug("multipart item = " + item.getFieldName().toUpperCase());

                        try {
                            logger.debug("XWPostParams.valueOf(" + item.getFieldName().toUpperCase() + ") = "
                                    + XWPostParams.valueOf(item.getFieldName().toUpperCase()));

                            switch (XWPostParams.valueOf(item.getFieldName().toUpperCase())) {
                            case DATAUID:
                                reqUri += "/" + item.getString();
                                break;
                            case DATAFILE:
                                dataUpload = item;
                                break;
                            case DATASHASUM:
                                dataUploadshasum = item.getString();
                                break;
                            }
                        } catch (final Exception e) {
                            logger.exception(e);
                        }
                    }
                }
            }

            for (final Enumeration<String> e = request.getParameterNames(); e.hasMoreElements();) {
                logger.debug("parameter " + e.nextElement());
            }
            for (final Enumeration<String> e = request.getHeaderNames(); e.hasMoreElements();) {
                logger.debug("header " + e.nextElement());
            }

            if (target.equals(PATH)) {
                sendDashboard(user);
                baseRequest.setHandled(true);
                return;
            }

            if (dataUpload != null) {
                final String value = request.getParameter(XWPostParams.DATASHASUM.toString());
                if (value != null) {
                    if (dataUploadshasum == null) {
                        dataUploadshasum = value;
                    }
                }
                logger.debug("Parameters upload size = " + dataUpload.getSize() + " dataUploadshasum = "
                        + dataUploadshasum);
            }

            final Iterator<String> it = paths.iterator();
            final StringBuilder uriWithoutCmd = new StringBuilder();
            int i = 0;
            while (it.hasNext()) {
                final String st = it.next();
                logger.debug("Parsing path path = " + st);
                if (i++ > 0) {
                    uriWithoutCmd.append("/" + st);
                }
            }

            final URI uri = new URI(Connection.httpsScheme() + "://" + XWTools.getHostName(request.getServerName())
                    + ":" + request.getServerPort() + uriWithoutCmd.toString());
            logger.debug("URI = " + uri);

            final String objXmlDesc = request.getParameter(XWPostParams.XMLDESC.toString());
            logger.finest("objXmlDesc = " + objXmlDesc);

            if (objXmlDesc != null) {
                final ByteArrayInputStream in = new ByteArrayInputStream(objXmlDesc.getBytes());
                try {
                    obj = Table.newInterface(in);
                } finally {
                    in.close();
                }
            }

            if (obj != null) {
                logger.debug("obj = " + obj.toXml());
                if (obj.getUID() == null) {
                    obj.setUID(new UID());
                }
            } else {
                logger.debug("obj = null");
            }
            command = getIdRpc().newCommand(uri, user, obj);
            command.setMandatingLogin(mandatingLogin);

            final String parameter = request.getParameter(XWPostParams.PARAMETER.toString());
            logger.debug("parameter = " + parameter);

            if (parameter != null) {
                switch (getIdRpc()) {
                case CHMOD: {
                    final XWAccessRights ar = new XWAccessRights(parameter);
                    ((XMLRPCCommandChmod) command).setModifier(ar);
                    break;
                }
                case ACTIVATEHOST: {
                    final Boolean b = new Boolean(parameter);
                    ((XMLRPCCommandActivateHost) command).setActivation(b);
                    break;
                }
                case WORKALIVE: {
                    final XMLHashtable p = new XMLHashtable(StreamIO.stream(parameter));
                    logger.debug("XMLHastable = " + (p == null ? "null" : p.toXml()));
                    ((XMLRPCCommandWorkAlive) command).setParameter(p);
                    break;
                }
                default:
                    break;
                }
            }
            command.getUser().setLogin(user.getLogin());
            command.getUser().setPassword(user.getPassword());
            command.getUser().setUID(user.getUID());
        } catch (final Exception e) {
            command = null;
            logger.exception(e);
            logger.error(e.toString());
        }

        try {
            if (command != null) {
                command.setRemoteName(request.getRemoteHost());
                command.setRemoteIP(request.getRemoteAddr());
                command.setRemotePort(request.getRemotePort());

                logger.debug("cmd = " + command.toXml());

                if (command.getIdRpc() == IdRpc.DOWNLOADDATA) {
                    final UID uid = command.getURI().getUID();
                    String contentTypeValue = CONTENTTYPEVALUE;

                    try {
                        final DataInterface theData = DBInterface.getInstance().getData(command);
                        final DataTypeEnum dataType = theData != null ? theData.getType() : null;
                        final Date lastModified = theData != null ? theData.getMTime() : null;
                        if (theData != null) {
                            if (lastModified != null) {
                                response.setHeader(LASTMODIFIEDLABEL, lastModified.toString());
                            }
                            response.setHeader(CONTENTLENGTHLABEL, "" + theData.getSize());
                            response.setHeader(CONTENTSHASUMLABEL, theData.getShasum());
                            response.setHeader(CONTENTDISPOSITIONLABEL,
                                    "attachment; filename=\"" + theData.getName() + "\"");
                        }
                        if (dataType != null) {
                            contentTypeValue = dataType.getMimeType();
                        }
                    } catch (final Exception e) {
                        if (logger.debug()) {
                            logger.exception(e);
                        }
                    }

                    logger.debug("set " + CONTENTTYPELABEL + " : " + contentTypeValue);
                    response.setHeader(CONTENTTYPELABEL, contentTypeValue);
                }

                super.run(command);
            } else {
                write((XMLable) null);
            }
        } finally {
            command = null;
            obj = null;
            baseRequest.setHandled(true);
            notifyAll();
        }
    }

    /**
     * This retrieves the user from its X509 certificate
     *
     * @throws IOException
     * @throws NoSuchAlgorithmException 
     */
    private UserInterface userFromCertificate(final HttpServletRequest request)
            throws IOException, NoSuchAlgorithmException {

        final Object certChain = request.getAttribute("javax.servlet.request.X509Certificate");

        if (certChain == null) {
            return null;
        }

        final Logger logger = getLogger();

        final UserInterface client = new UserInterface();

        final X509Certificate certs[] = (X509Certificate[]) certChain;
        int i = 0;
        for (final X509Certificate cert : certs) {
            final String dn = cert.getSubjectDN().getName();
            logger.debug("SubjectDN[" + i++ + "] = " + dn);
            try {
                final String dnu = dn.toUpperCase();
                final int idx = dnu.indexOf(DNHEADER_EMAIL);
                if (idx != -1) {
                    final int startemail = idx + DNHEADERLENGTH_EMAIL;
                    int endemail = dnu.indexOf(',', startemail);
                    if (endemail == -1) {
                        endemail = dnu.length() - startemail;
                    }
                    final String email = dn.substring(startemail, endemail);
                    logger.debug("email = " + email);
                    if (email != null) {
                        client.setEMail(email);
                    }
                }
            } catch (final Exception e) {
            }
        }
        final String subjectName = certs[0].getSubjectX500Principal().getName();
        final String issuerName = certs[0].getIssuerX500Principal().getName();
        final String loginName = subjectName + "_" + issuerName;
        final String random = loginName + Math.random() + System.currentTimeMillis();
        final String shastr = XWTools.sha256(random);
        client.setLogin(loginName); // login may be truncated; see
        // UserIntergace.USERLOGINLENGTH

        final UserInterface ret = DBInterface.getInstance()
                .user(UserInterface.Columns.LOGIN.toString() + "= '" + client.getLogin() + "'");

        if (ret != null) {
            ret.setEMail(client.getEMail());
            logger.debug(("user = " + ret) == null ? "null" : ret.toXml());
            return ret;
        }

        final UserInterface admin = Dispatcher.getConfig().getProperty(XWPropertyDefs.ADMINLOGIN) == null ? null
                : DBInterface.getInstance()
                        .user(SQLRequest.MAINTABLEALIAS + "." + UserInterface.Columns.LOGIN.toString() + "='"
                                + Dispatcher.getConfig().getProperty(XWPropertyDefs.ADMINLOGIN) + "'");
        if (admin == null) {
            throw new IOException("can't insert new certified user");
        }

        client.setUID(new UID());
        client.setLogin(loginName);
        client.setPassword(shastr);
        if (client.getEMail() == null) {
            client.setEMail(loginName);
        }
        client.setOwner(Dispatcher.getConfig().getAdminUid());
        client.setRights(UserRightEnum.STANDARD_USER);

        try {
            DBInterface.getInstance().addUser(admin, client);
            return client;
        } catch (final Exception e) {
            throw new IOException("user certification error : " + e.getMessage());
        }
    }

    /**
     * This retrieves the user from a remove OpenId server
     * 
     * @deprecated since 10.5.0, OAuth is preferred
     */
    @Deprecated
    private UserInterface userFromOpenId(final HttpServletRequest request) throws IOException {

        final HttpSession session = request.getSession(true);
        final String authNonce = (request.getParameter(XWPostParams.AUTH_NONCE.toString()) != null
                ? request.getParameter(XWPostParams.AUTH_NONCE.toString())
                : (String) session.getAttribute(XWPostParams.AUTH_NONCE.toString()));
        final String authEmail = (request.getParameter(XWPostParams.AUTH_EMAIL.toString()) != null
                ? request.getParameter(XWPostParams.AUTH_EMAIL.toString())
                : (String) session.getAttribute(XWPostParams.AUTH_EMAIL.toString()));
        final String authId = (request.getParameter(XWPostParams.AUTH_IDENTITY.toString()) != null
                ? request.getParameter(XWPostParams.AUTH_IDENTITY.toString())
                : (String) session.getAttribute(XWPostParams.AUTH_IDENTITY.toString()));

        if ((authNonce == null) || (authEmail == null)) {
            return null;
        }

        UserInterface ret = null;

        try {
            HTTPOpenIdHandler.getInstance().verifyNonce(authNonce);
            ret = DBInterface.getInstance().user(UserInterface.Columns.EMAIL.toString() + "= '" + authEmail + "'");
            if (ret == null) {
                ret = newUser(authId, authEmail);
                /*
                if (Dispatcher.getConfig().getBoolean(XWPropertyDefs.DELEGATEDREGISTRATION) == false) {
                   throw new IOException("delegated registration is not allowed");
                }
                    
                final UserInterface admin = (Dispatcher.getConfig().getProperty(XWPropertyDefs.ADMINLOGIN) == null ? null
                      : DBInterface.getInstance()
                      .user(SQLRequest.MAINTABLEALIAS + "." + UserInterface.Columns.LOGIN.toString() + "='"
                    + Dispatcher.getConfig().getProperty(XWPropertyDefs.ADMINLOGIN) + "'"));
                if (admin == null) {
                   throw new IOException("can't insert new OpenId user (cant't retrieve admin)");
                }
                final String random = authEmail + System.currentTimeMillis() + Math.random();
                final String shastr = XWTools.sha256(random);
                final UserInterface client = new UserInterface();
                client.setUID(new UID());
                client.setOwner(Dispatcher.getConfig().getAdminUid());
                client.setLogin(authId);
                client.setPassword(shastr);
                client.setRights(UserRightEnum.STANDARD_USER);
                client.setEMail(authEmail);
                DBInterface.getInstance().addUser(admin, client);
                ret = client;
                */
            }
            session.setAttribute(XWPostParams.AUTH_NONCE.toString(), authNonce);
            session.setAttribute(XWPostParams.AUTH_EMAIL.toString(), authEmail);
        } catch (final Exception e) {
            getLogger().exception("openid delegation error", e);
            throw new IOException("openid delegation error : " + e.getMessage());
        }

        return ret;
    }

    /**
     * This retrieves the user from a remove OAuth server
     * @throws IOException 
     * @throws NoSuchAlgorithmException 
     */
    private UserInterface userFromJWTEthereumAuth(final HttpServletRequest request)
            throws IOException, NoSuchAlgorithmException {

        final HttpSession session = request.getSession(true);

        final String ethauthState = (request.getParameter(XWPostParams.AUTH_STATE.toString()) != null
                ? request.getParameter(XWPostParams.AUTH_STATE.toString())
                : (String) session.getAttribute(XWPostParams.AUTH_STATE.toString()));

        getLogger().debug("eth auth state = " + ethauthState);

        if (ethauthState == null) {
            return null;
        }

        final DecodedJWT jwt = HTTPJWTEthereumAuthHandler.getInstance()
                .getToken(HTTPJWTEthereumAuthHandler.getInstance().getState(ethauthState));
        if (jwt == null) {
            throw new IOException("JWT error");
        }
        final String authAddr = ((HTTPJWTEthereumAuthHandler) HTTPJWTEthereumAuthHandler.getInstance())
                .getEthereumAddress(jwt);

        UserInterface ret = DBInterface.getInstance()
                .user(UserInterface.Columns.LOGIN.toString() + "= '" + authAddr + "'");
        getLogger().debug("userFromJWTEthereumAuth ret = " + (ret == null ? "null" : ret.toXml()));
        if (ret == null) {
            try {
                ret = newUser(authAddr);
            } catch (InvalidKeyException | AccessControlException e) {
                getLogger().exception(e);
                throw new IOException(e.toString());
            }
        }
        session.setAttribute(XWPostParams.AUTH_EMAIL.toString(), authAddr);
        session.setAttribute(XWPostParams.AUTH_STATE.toString(), ethauthState);

        return ret;
    }

    /**
     * This retrieves the user from a remove OAuth server
     * @throws NoSuchAlgorithmException 
     * @throws AccessControlException 
     * @throws InvalidKeyException 
     * @throws OAuthException 
     */
    private UserInterface userFromOAuth(final HttpServletRequest request)
            throws IOException, NoSuchAlgorithmException {

        final HttpSession session = request.getSession(true);
        final String authState = (request.getParameter(XWPostParams.AUTH_STATE.toString()) != null
                ? request.getParameter(XWPostParams.AUTH_STATE.toString())
                : (String) session.getAttribute(XWPostParams.AUTH_STATE.toString()));
        final String authEmail = (request.getParameter(XWPostParams.AUTH_EMAIL.toString()) != null
                ? request.getParameter(XWPostParams.AUTH_EMAIL.toString())
                : (String) session.getAttribute(XWPostParams.AUTH_EMAIL.toString()));
        final String authId = (request.getParameter(XWPostParams.AUTH_IDENTITY.toString()) != null
                ? request.getParameter(XWPostParams.AUTH_IDENTITY.toString())
                : (String) session.getAttribute(XWPostParams.AUTH_IDENTITY.toString()));

        getLogger().debug("oauthState = " + authState);
        getLogger().debug("oauthEmail= " + authEmail);
        getLogger().debug("oauthId= " + authId);

        if ((authState == null) || (authEmail == null)) {
            return null;
        }

        UserInterface ret = null;
        try {
            HTTPOAuthHandler.getInstance().checkState(authState);
            ret = DBInterface.getInstance().user(UserInterface.Columns.EMAIL.toString() + "= '" + authEmail + "'");
            if (ret == null) {
                ret = newUser(authId, authEmail);
            }
        } catch (OAuthException | InvalidKeyException | AccessControlException e) {
            throw new IOException(e.toString());
        }
        session.setAttribute(XWPostParams.AUTH_EMAIL.toString(), authEmail);
        session.setAttribute(XWPostParams.AUTH_STATE.toString(), authState);
        session.setAttribute(XWPostParams.AUTH_IDENTITY.toString(), authId);

        return ret;
    }

    /**
     * This retrieves the user from login/password
     *
     * @since 10.2.0
     */
    private UserInterface userFromPostParams(final HttpServletRequest request) throws IOException {

        final HttpSession session = request.getSession(true);

        final String login = (request.getParameter(XWPostParams.XWLOGIN.toString()) != null
                ? request.getParameter(XWPostParams.XWLOGIN.toString())
                : (String) session.getAttribute(XWPostParams.XWLOGIN.toString()));
        final String passwd = (request.getParameter(XWPostParams.XWPASSWD.toString()) != null
                ? request.getParameter(XWPostParams.XWPASSWD.toString())
                : (String) session.getAttribute(XWPostParams.XWPASSWD.toString()));

        if ((login == null) || (passwd == null)) {
            return null;
        }

        try {
            final UserInterface ret = DBInterface.getInstance()
                    .user(SQLRequest.MAINTABLEALIAS + "." + UserInterface.Columns.LOGIN.toString() + "='" + login
                            + "' AND " + SQLRequest.MAINTABLEALIAS + "." + UserInterface.Columns.PASSWORD.toString()
                            + "='" + passwd + "'");

            session.setAttribute(XWPostParams.XWLOGIN.toString(), login);
            session.setAttribute(XWPostParams.XWPASSWD.toString(), passwd);
            return ret;
        } catch (final Exception e) {
            throw new IOException("userFromPostParams error : " + e.getMessage());
        }
    }

    /**
     * This retrieves the user from login/password inside Authorization header
     *
     * @since 10.2.0
     */
    private UserInterface userFromBasicAuth(final HttpServletRequest request) throws IOException {
        final HttpSession session = request.getSession(true);

        String login = null;
        String password = null;
        ;

        final String authorization = request.getHeader("Authorization");
        if (authorization != null && authorization.startsWith("Basic")) {
            // Authorization: Basic base64credentials
            String base64Credentials = authorization.substring("Basic".length()).trim();
            String credentials = new String(Base64.getDecoder().decode(base64Credentials),
                    Charset.forName("UTF-8"));
            // credentials = username:password
            final String[] values = credentials.split(":", 2);

            if (values != null && values.length > 1) {
                login = values[0];
                password = values[1];
            }

            if ((login == null) || (password == null)) {
                return null;
            }

            try {
                final UserInterface ret = DBInterface.getInstance()
                        .user(SQLRequest.MAINTABLEALIAS + "." + UserInterface.Columns.LOGIN.toString() + "='"
                                + login + "' AND " + SQLRequest.MAINTABLEALIAS + "."
                                + UserInterface.Columns.PASSWORD.toString() + "='" + password + "'");

                session.setAttribute(XWPostParams.XWLOGIN.toString(), login);
                session.setAttribute(XWPostParams.XWPASSWD.toString(), password);
                return ret;
            } catch (final Exception e) {
                throw new IOException("userFromPostParams error : " + e.getMessage());
            }
        }
        return null;
    }

    /**
     * This calls newUser(login, null)
     * @param login is the user login
     * @return a new user
     * @throws IOException 
     * @throws AccessControlException 
     * @throws InvalidKeyException 
     * @throws NoSuchAlgorithmException 
     * @since 11.0.0
     */
    private UserInterface newUser(String login)
            throws IOException, InvalidKeyException, AccessControlException, NoSuchAlgorithmException {
        return newUser(login, "");
    }

    /**
     * @param login is the new user login
     * @param emailaddr is the new user email address
     * @return a new user
     * @throws IOException 
     * @throws AccessControlException 
     * @throws InvalidKeyException 
     * @throws NoSuchAlgorithmException 
     * @since 11.0.0
     */
    private UserInterface newUser(String login, final String emailaddr)
            throws IOException, InvalidKeyException, AccessControlException, NoSuchAlgorithmException {
        if (!Dispatcher.getConfig().getBoolean(XWPropertyDefs.DELEGATEDREGISTRATION)) {
            throw new AccessControlException("DELEGATEDREGISTRATION is not allowed");
        }

        final UserInterface admin = Dispatcher.getConfig().getProperty(XWPropertyDefs.ADMINLOGIN) == null ? null
                : DBInterface.getInstance()
                        .user(SQLRequest.MAINTABLEALIAS + "." + UserInterface.Columns.LOGIN.toString() + "='"
                                + Dispatcher.getConfig().getProperty(XWPropertyDefs.ADMINLOGIN) + "'");
        if (admin == null) {
            throw new AccessControlException("can't insert new OAuth user");
        }

        final String random = emailaddr + System.currentTimeMillis() + Math.random();
        final String shastr = XWTools.sha256(random);
        final UserInterface client = new UserInterface();
        client.setUID(new UID());
        client.setOwner(Dispatcher.getConfig().getAdminUid());
        client.setLogin(login);
        client.setPassword(shastr);
        client.setRights(UserRightEnum.STANDARD_USER);
        client.setEMail(emailaddr);
        DBInterface.getInstance().addUser(admin, client);

        return client;
    }

    /**
     * This uploads a data to server<br />
     * Data must be defined on server side (i.e. sendData() must be called
     * first)
     *
     * @param command
     *            is the command
     */
    @Override
    public synchronized long uploadData(final XMLRPCCommand command)
            throws IOException, InvalidKeyException, AccessControlException {

        final DataInterface theData = (DataInterface) get(command);
        final UID uid = command.getURI().getUID();

        long ret = 0;
        if (dataUpload.getSize() > XWTools.MAXFILESIZE) {
            throw new IOException(theData.getPath() + " upload file too long");
        }

        mileStone("<uploadData>");

        try {
            if (theData == null) {
                mileStone("<error method='uploadData' msg='no data' />");
                mileStone("</uploadData>");
                notifyAll();
                throw new RemoteException("uploadData(" + uid + ") data not found");
            }

            final File dFile = theData.getPath();

            if (dataUpload == null) {
                mileStone("<error method='uploadData' msg='no data upload' />");
                mileStone("</uploadData>");
                notifyAll();
                throw new IOException("upload is null");
            }
            dataUpload.write(dFile);
            final long fsize = dFile.length();
            final String shasum = XWTools.sha256CheckSum(dFile);
            if (fsize != dataUpload.getSize()) {
                dFile.delete();
                throw new IOException(
                        "Upload file size error should be " + dataUpload.getSize() + " but found " + fsize);
            }
            if ((dataUploadshasum == null) || (dataUploadshasum.compareToIgnoreCase(shasum) != 0)) {
                dFile.delete();
                throw new IOException(
                        "Upload file SHASUM error should be " + dataUploadshasum + " but found " + shasum);
            }
            theData.setSize(fsize);
            theData.setShasum(shasum);
            theData.setStatus(StatusEnum.AVAILABLE);
            theData.update();
            ret = dFile.length();
        } catch (final InvalidKeyException | AccessControlException e) {
            mileStone("<error method='uploadData' msg='" + e.getMessage() + "' />");
            throw e;
        } catch (final Exception e) {
            try {
                if (theData != null) {
                    theData.setStatus(StatusEnum.ERROR);
                    theData.update();
                }
            } catch (final Exception e2) {
                getLogger().exception(e2);
            }

            getLogger().exception(e);
            mileStone("<error method='uploadData' msg='" + e.getMessage() + "' />");
            throw new RemoteException(e.toString());
        } finally {
            dataUpload = null;
            dataUploadshasum = null;
            mileStone("</uploadData>");
            notifyAll();
        }
        return ret;
    }

    /**
     * This cleans and closes communications
     */
    @Override
    public void close() {

        mileStone("<close>");

        try {
            request.getInputStream().close();
        } catch (final Exception e) {
        }

        try {
            response.getWriter().flush();
        } catch (final Exception e) {
        }

        try {
            response.getWriter().close();
        } catch (final Exception e) {
        }

        mileStone("</close>");
    }
}