org.syncany.plugins.php.PhpTransferManager.java Source code

Java tutorial

Introduction

Here is the source code for org.syncany.plugins.php.PhpTransferManager.java

Source

/*
 * Syncany, www.syncany.org
 * Copyright (C) 2015 Hans Dijkema <hans@dykema.nl>
 *
 * This program 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.syncany.plugins.php;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.swing.JOptionPane;

import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.syncany.config.Config;
import org.syncany.plugins.transfer.AbstractTransferManager;
import org.syncany.plugins.transfer.StorageException;
import org.syncany.plugins.transfer.files.ActionRemoteFile;
import org.syncany.plugins.transfer.files.CleanupRemoteFile;
import org.syncany.plugins.transfer.files.DatabaseRemoteFile;
import org.syncany.plugins.transfer.files.MultichunkRemoteFile;
import org.syncany.plugins.transfer.files.RemoteFile;
import org.syncany.plugins.transfer.files.SyncanyRemoteFile;
import org.syncany.plugins.transfer.files.TempRemoteFile;
import org.syncany.plugins.transfer.files.TransactionRemoteFile;

public class PhpTransferManager extends AbstractTransferManager {
    private static final Logger logger = Logger.getLogger(PhpTransferManager.class.getSimpleName());

    private interface IPost {
        /**
         * Give a change to add name value pairs.
         */
        public void mutateNVPS(List<NameValuePair> nvps) throws Exception;

        /**
         * Returns the expected maximum length of this post, or -1 if no restrictions
         */
        public int mutatePost(HttpPost p) throws Exception;

        /**
         * Consume the response, return a code about the response
         */
        public int consumeResponse(InputStream s) throws Exception;
    };

    public String getAnswer(InputStream s) throws Exception {
        StringBuffer line = new StringBuffer();
        int c = s.read();
        while (c != -1 && c != '\n') {
            line.append(Character.toChars(c));
            c = s.read();
        }
        return line.toString();
    }

    public String getAnswer(InputStream s, int maxlen) throws Exception {
        StringBuffer line = new StringBuffer();
        int c = s.read();
        int t = 1;
        while (c != -1 && c != '\n' && t < maxlen) {
            line.append(Character.toChars(c));
            t = t + 1;
            c = s.read();
        }
        return line.toString();
    }

    private String lastSite;

    public PhpTransferManager(PhpTransferSettings settings, Config config) throws Exception {
        super(settings, config);
        /*      
              // Create a trust manager that does not validate certificate chains
              TrustManager[] trustAllCerts = new TrustManager[]{
                  new X509TrustManager() {
          public java.security.cert.X509Certificate[] getAcceptedIssuers() {
              return null;
          }
              
          public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
          }
              
          public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
             if (lastSite != null && !lastSite.equals("")) {
                int r = JOptionPane.showConfirmDialog(null, lastSite + "'s SSL certificate is not trusted, do you want to accept it?",
                                           "Accept SSL Certificate?",
                                           JOptionPane.YES_NO_OPTION,
                                           JOptionPane.QUESTION_MESSAGE);
                if (r == 0) {
                       
                } else {
                       
                }
             }
          }
                  }
              };
            
              // Install the all-trusting trust manager
              try {
                  SSLContext sc = SSLContext.getInstance("SSL");
                  sc.init(null, trustAllCerts, new java.security.SecureRandom());
                  HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
              } catch (Exception e) {
                 logger.log(Level.SEVERE, "Cannot install SSL trust manager", e);
              }
              */

    }

    public PhpTransferSettings getSettings() {
        return (PhpTransferSettings) settings;
    }

    @Override
    public void connect() throws StorageException {
        // Nothing
    }

    @Override
    public void disconnect() {
        // Nothing
    }

    @Override
    public void init(boolean createIfRequired) throws StorageException {
        logger.info("init plugin");
        if (createIfRequired) {
        } else {
        }
    }

    @Override
    public void download(RemoteFile remoteFile, File localFile) throws StorageException {
        logger.info("Downloading: " + remoteFile.getName() + " to " + localFile.getName());
        try {
            final String remote_name = remoteFile.getName();
            final File f = localFile;
            int r = operate("download", new IPost() {
                public void mutateNVPS(List<NameValuePair> nvps) throws Exception {
                    nvps.add(new BasicNameValuePair("filename", remote_name));
                }

                public int mutatePost(HttpPost p) throws Exception {
                    return -1;
                }

                public int consumeResponse(InputStream s) throws Exception {
                    String answer = getAnswer(s);
                    if (answer.equals("true")) {
                        logger.info("writing outputstream for file " + f);
                        OutputStream out = new FileOutputStream(f);
                        logger.info("File opened");
                        byte[] buf = new byte[10240];
                        int len = s.read(buf);
                        int total = len;
                        while (len > 0) {
                            out.write(buf, 0, len);
                            len = s.read(buf);
                            total += len;
                        }
                        logger.info("read " + total + " bytes");
                        out.close();
                        logger.info("done writing");
                        return 1;
                    } else {
                        throw new Exception(answer);
                    }
                }

            });
        } catch (Exception e) {
            throw new StorageException("Cannot download file " + remoteFile, e);
        }
    }

    @Override
    public void upload(File localFile, RemoteFile remoteFile) throws StorageException {
        logger.info("Uploading: " + localFile.getName() + " to " + remoteFile.getName());
        try {
            final String remote_name = remoteFile.getName();
            final File f = localFile;
            int r = operate("upload", new IPost() {
                List<NameValuePair> _nvps;

                public void mutateNVPS(List<NameValuePair> nvps) throws Exception {
                    _nvps = nvps;
                    nvps.add(new BasicNameValuePair("filename", remote_name));
                }

                public int mutatePost(HttpPost p) throws Exception {
                    MultipartEntityBuilder builder = MultipartEntityBuilder.create();
                    builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
                    builder.addPart("file", new FileBody(f));
                    Iterator<NameValuePair> it = _nvps.iterator();
                    while (it.hasNext()) {
                        NameValuePair nvp = it.next();
                        builder.addTextBody(nvp.getName(), nvp.getValue());
                    }
                    p.setEntity(builder.build());
                    return -1;
                }

                @Override
                public int consumeResponse(InputStream s) throws Exception {
                    String response = getAnswer(s);
                    if (response.equals("true")) {
                        return 1;
                    } else {
                        throw new Exception(response);
                    }
                }

            });
            if (r != 1) {
                throw new Exception("Unexpected error, result code = " + r);
            }

        } catch (Exception e) {
            throw new StorageException("Cannot upload file " + remoteFile, e);
        }
    }

    @Override
    public boolean delete(RemoteFile _remoteFile) throws StorageException {
        try {
            final RemoteFile f = _remoteFile;
            logger.info("Deleting remote file: " + f.toString());
            int r = operate("delete", new IPost() {
                public int mutatePost(HttpPost p) throws Exception {
                    return -1;
                }

                public void mutateNVPS(List<NameValuePair> nvps) throws Exception {
                    nvps.add(new BasicNameValuePair("filename", f.getName()));
                }

                public int consumeResponse(InputStream s) throws Exception {
                    String response = getAnswer(s, 10);
                    if (response.equals("true")) {
                        return 1;
                    } else {
                        return 0;
                    }
                }
            });
            return (r == 1) ? true : false;
        } catch (Exception e) {
            logger.log(Level.WARNING, "Cannot delete remote file " + _remoteFile + ". IGNORING.", e);
            return false;
        }
    }

    @Override
    public void move(RemoteFile sourceFile, RemoteFile targetFile) throws StorageException {
        try {
            final RemoteFile f = sourceFile;
            final RemoteFile g = targetFile;
            logger.info("Moving remote file: " + f.toString() + " to " + g.toString());
            int r = operate("move", new IPost() {
                public int mutatePost(HttpPost p) throws Exception {
                    return -1;
                }

                public void mutateNVPS(List<NameValuePair> nvps) throws Exception {
                    nvps.add(new BasicNameValuePair("filename", f.getName()));
                    nvps.add(new BasicNameValuePair("to_filename", g.getName()));
                }

                public int consumeResponse(InputStream s) throws Exception {
                    String response = getAnswer(s, 10);
                    if (response.equals("true")) {
                        return 1;
                    } else {
                        return 0;
                    }
                }
            });
            logger.info("result = " + r);
        } catch (Exception e) {
            throw new StorageException(e);
        }
    }

    private String getRemoteFilePath(Class<? extends RemoteFile> remoteFile) {
        if (remoteFile.equals(MultichunkRemoteFile.class)) {
            return "multichunk";
        } else if (remoteFile.equals(DatabaseRemoteFile.class)) {
            return "database";
        } else if (remoteFile.equals(CleanupRemoteFile.class)) {
            return "cleanup";
        } else if (remoteFile.equals(ActionRemoteFile.class)) {
            return "action";
        } else if (remoteFile.equals(TransactionRemoteFile.class)) {
            return "transaction";
        } else if (remoteFile.equals(TempRemoteFile.class)) {
            return "temp";
        } else {
            return "";
        }
    }

    @Override
    public <T extends RemoteFile> Map<String, T> list(Class<T> remoteFileClass) throws StorageException {
        try {
            final Map<String, T> fileList = new HashMap<String, T>();
            final String listType = getRemoteFilePath(remoteFileClass);
            final Class<T> _remoteFileClass = remoteFileClass;
            logger.info("Listing remote files for type: " + listType);
            int r = operate("list", new IPost() {
                public int mutatePost(HttpPost p) throws Exception {
                    return -1;
                }

                public void mutateNVPS(List<NameValuePair> nvps) throws Exception {
                    nvps.add(new BasicNameValuePair("type", listType));
                }

                public int consumeResponse(InputStream s) throws Exception {
                    String response = getAnswer(s, 10);
                    if (response.equals("true")) {
                        byte[] buf = new byte[10240];
                        int len = s.read(buf);
                        String prev = "";
                        while (len > 0) {
                            String str = prev + new String(buf, 0, len, Charset.forName("UTF-8"));
                            logger.info("Got " + str);
                            String[] parts = str.split("\\s+");
                            logger.info("parts : " + parts.length);
                            int i;
                            for (i = 0; i < parts.length - 1; i++) {
                                try {
                                    logger.info("part[" + i + "]=" + parts[i]);
                                    T remoteFile = RemoteFile.createRemoteFile(parts[i], _remoteFileClass);
                                    fileList.put(parts[i], remoteFile);
                                } catch (Exception e) {
                                    logger.log(Level.INFO,
                                            "Cannot create instance of " + _remoteFileClass.getSimpleName()
                                                    + " for file " + parts[i]
                                                    + "; maybe invalid file name pattern. Ignoring file.");
                                }
                            }
                            prev = parts[i];
                            logger.info("prev = " + prev);
                            len = s.read(buf);
                            logger.info("len = " + len);
                        }
                        if (!prev.equals("")) {
                            try {
                                logger.info("prev = " + prev);
                                T remoteFile = RemoteFile.createRemoteFile(prev, _remoteFileClass);
                                fileList.put(prev, remoteFile);
                            } catch (Exception e) {
                                logger.log(Level.INFO,
                                        "Cannot create instance of " + _remoteFileClass.getSimpleName()
                                                + " for file " + prev
                                                + "; maybe invalid file name pattern. Ignoring file.");
                            }
                        }
                        return 1;
                    } else {
                        return 0;
                    }
                }
            });
            if (r == 1) {
                return fileList;
            } else {
                throw new Exception("Cannot list files remote");
            }
        } catch (Exception e) {
            throw new StorageException(e);
        }
    }

    @Override
    public boolean testTargetCanWrite() {
        return testTargetLogin();
    }

    @Override
    public boolean testTargetExists() {
        return testTargetLogin();
    }

    @Override
    public boolean testTargetCanCreate() {
        return testTargetLogin();
    }

    @Override
    public boolean testRepoFileExists() {
        try {
            final RemoteFile f = new SyncanyRemoteFile();
            logger.info("Checking for remote file: " + f.toString());
            int r = operate("exists", new IPost() {
                public int mutatePost(HttpPost p) throws Exception {
                    return -1;
                }

                public void mutateNVPS(List<NameValuePair> nvps) throws Exception {
                    nvps.add(new BasicNameValuePair("filename", f.getName()));
                }

                public int consumeResponse(InputStream s) throws Exception {
                    String response = getAnswer(s, 10);
                    if (response.equals("true")) {
                        return 1;
                    } else {
                        return 0;
                    }
                }
            });
            return (r == 1) ? true : false;
        } catch (Exception e) {
            logger.log(Level.SEVERE, e.getMessage(), e);
            return false;
        }
    }

    public boolean testTargetLogin() {
        try {
            int r = operate("login", new IPost() {
                public void mutateNVPS(List<NameValuePair> nvps) throws Exception {
                }

                public int mutatePost(HttpPost p) throws Exception {
                    return -1;
                }

                public int consumeResponse(InputStream s) throws Exception {
                    String response = getAnswer(s);
                    if (response.equals("true")) {
                        return 1;
                    } else {
                        logger.log(Level.SEVERE, "response=" + response);
                        return 0;
                    }
                }
            });
            return (r == 1) ? true : false;
        } catch (Exception e) {
            logger.log(Level.SEVERE, e.getMessage(), e);
            return false;
        }
    }

    @SuppressWarnings("deprecation")
    private CloseableHttpClient getHttpClient() {
        try {
            @SuppressWarnings("deprecation")
            SSLSocketFactory sf = new SSLSocketFactory(new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    if (lastSite != null && !lastSite.equals("")) {
                        Preferences prefs = Preferences.userRoot().node(this.getClass().getName());
                        int prevr = prefs.getInt(lastSite, -1);
                        if (prevr == -1) {
                            int r = JOptionPane.showConfirmDialog(null,
                                    lastSite + "'s SSL certificate is not trusted, do you want to accept it?",
                                    "Accept SSL Certificate?", JOptionPane.YES_NO_OPTION,
                                    JOptionPane.QUESTION_MESSAGE);
                            logger.warning(lastSite + " not trusted, user answered " + r);
                            prevr = r;
                            prefs.putInt(lastSite, r);
                        }
                        logger.warning(lastSite + " not trusted, registered user answer: " + prevr);
                        if (prevr == 0) {
                            return true;
                        } else {
                            return false;
                        }
                    } else {
                        return false;
                    }
                }
            });
            @SuppressWarnings("deprecation")
            SchemeRegistry registry = new SchemeRegistry();
            registry.register(new Scheme("https", 443, sf));
            @SuppressWarnings("deprecation")
            ClientConnectionManager ccm = new ThreadSafeClientConnManager(registry);
            return new DefaultHttpClient(ccm);
        } catch (Exception e) {
            return new DefaultHttpClient();
        }
    }

    private int operate(String action, IPost mutator) throws Exception {
        String url = settings.getField("url") + "/syncany_php.php";
        String context = settings.getField("context");
        String userid = settings.getField("userid");
        String passwd = settings.getField("passwd");

        logger.info("url: " + url);
        logger.info("action: " + action);
        logger.info("userid: " + userid + ", context: " + context);
        lastSite = url;

        HttpPost p = new HttpPost(url);

        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        nvps.add(new BasicNameValuePair("userid", userid));
        nvps.add(new BasicNameValuePair("passwd", passwd));
        nvps.add(new BasicNameValuePair("context", context));
        nvps.add(new BasicNameValuePair("action", action));
        mutator.mutateNVPS(nvps);
        p.setEntity(new UrlEncodedFormEntity(nvps));

        int maxlen = mutator.mutatePost(p);
        int responsecode = -1;

        try {
            CloseableHttpClient httpclient = getHttpClient(); //HttpClients.createDefault();
            CloseableHttpResponse response = httpclient.execute(p);
            HttpEntity e = response.getEntity();
            long len = e.getContentLength();
            logger.info("length of response: " + len);
            StatusLine sl = response.getStatusLine();
            int code = sl.getStatusCode();
            if (code != 200) {
                response.close();
                httpclient.close();
                throw new Exception("Response on http call: " + code);
            } else {
                if ((maxlen >= 0) && ((len >= maxlen) || (len < 0))) {
                    response.close();
                    httpclient.close();
                    throw new Exception("Expected maximum length: " + maxlen + ", returned content length: " + len);
                } else {
                    try {
                        InputStream b = e.getContent();
                        responsecode = mutator.consumeResponse(b);
                        b.close();
                        response.close();
                        httpclient.close();
                    } catch (Exception ex) {
                        response.close();
                        httpclient.close();
                        throw new Exception(ex);
                    }
                }
            }
            response.close();
            httpclient.close();
        } catch (Exception e) {
            throw new Exception(e);
        }
        return responsecode;
    }

}