org.jared.synodroid.ds.server.SimpleSynoServer.java Source code

Java tutorial

Introduction

Here is the source code for org.jared.synodroid.ds.server.SimpleSynoServer.java

Source

/**
 * Copyright 2010 Eric Taix Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language governing permissions and limitations under the
 * License.
 */
package org.jared.synodroid.ds.server;

import java.io.BufferedReader;
import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketException;
import java.net.URL;
import java.security.SecureRandom;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManager;

import org.jared.synodroid.ds.Synodroid;
import org.jared.synodroid.ds.protocol.DSMException;
import org.jared.synodroid.ds.protocol.DSMHandlerFactory;
import org.jared.synodroid.ds.protocol.MultipartBuilder;
import org.jared.synodroid.ds.protocol.https.AcceptAllHostNameVerifier;
import org.jared.synodroid.ds.protocol.https.AcceptAllTrustManager;
import org.jared.synodroid.ds.utils.GenericException;
import org.jared.synodroid.ds.utils.ServerParam;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import org.jared.synodroid.ds.data.DSMVersion;

import android.net.Uri;
import android.util.Log;

/**
 * This is a light weight class for the synology server. This class assumes that you are already connected and reuse the cookie of 
 * the normal SynoServer class to to its transaction. This is used by content providers and external intents.
 * 
 * @author Steve Garon
 */
public class SimpleSynoServer {
    public boolean DEBUG = false;
    protected String cookies = "";
    protected DSMHandlerFactory dsmFactory;
    protected DSMVersion dsmVersion = DSMVersion.VERSION2_2;
    protected boolean autoDetect = false;
    protected String url;

    /**
     * Static intialization of the SSL factory to accept each certificate, even if a certificate is self signed
     */
    static {
        SSLContext sc;
        try {
            sc = SSLContext.getInstance("TLS");
            sc.init(null, new TrustManager[] { new AcceptAllTrustManager() }, new SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
            HttpsURLConnection.setDefaultHostnameVerifier(new AcceptAllHostNameVerifier());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * Constructor which set all server's informations. No connection are made when calling the constructor.
     */
    public SimpleSynoServer() {
    }

    /**
     * This is the actual initialisation function
     * @param params
     */
    public void setParams(ServerParam params) {
        cookies = params.getCookie();
        url = params.getUrl();
        dsmVersion = DSMVersion.titleOf(params.getDSMVersion());
        autoDetect = false;
        DEBUG = params.getDbg();
        dsmFactory = DSMHandlerFactory.getFactory(dsmVersion, this, DEBUG, autoDetect);
    }

    /**
     * Return the handler factory
     * 
     * @return
     */
    public DSMHandlerFactory getDSMHandlerFactory() {
        return dsmFactory;
    }

    /**
     * @return the dsmVersion
     */
    public DSMVersion getDsmVersion() {
        return dsmVersion;
    }

    public String getUrl() {
        return url;
    }

    public void setCookie(String cookieP) {
        cookies = cookieP;
    }

    public String getCookies() {
        return cookies;
    }

    /**
     * Create a connection and add all required cookies information
     * 
     * @param uriP
     * @param requestP
     * @param methodP
     * @return
     * @throws MalformedURLException
     * @throws IOException
     */
    protected HttpURLConnection createConnection(String uriP, String requestP, String methodP, boolean log)
            throws MalformedURLException, IOException {
        // Prepare the connection
        HttpURLConnection con = (HttpURLConnection) new URL(getUrl() + Uri.encode(uriP, "/")).openConnection();

        // Add cookies if exist
        if (cookies != null) {
            con.addRequestProperty("Cookie", getCookies());
            if (DEBUG)
                Log.v(Synodroid.DS_TAG, "Added cookie to the request: " + cookies);
        }
        con.setDoOutput(true);
        con.setDoInput(true);
        con.setUseCaches(false);
        con.setRequestMethod(methodP);
        con.setConnectTimeout(20000);
        if (DEBUG) {
            if (log) {
                Log.i(Synodroid.DS_TAG, methodP + ": " + uriP + "?" + requestP);
            } else {
                Log.i(Synodroid.DS_TAG, methodP + ": " + uriP + " (hidden request)");
            }
        }
        return con;
    }

    /**
     * Send a request to the server.
     * 
     * @param uriP
     *            The part of the URI ie: /webman/doit.cgi
     * @param requestP
     *            The query in the form 'param1=foo&param2=yes'
     * @param methodP
     *            The method to send this request
     * @return A JSONObject containing the response of the server
     * @throws DSMException
     */
    public JSONObject sendJSONRequest(String uriP, String requestP, String methodP) throws Exception {
        return sendJSONRequest(uriP, requestP, methodP, true);
    }

    /**
     * Send a request to the server.
     * 
     * @param uriP
     *            The part of the URI ie: /webman/doit.cgi
     * @param requestP
     *            The query in the form 'param1=foo&param2=yes'
     * @param methodP
     *            The method to send this request
     * @return A JSONArray containing the response of the server
     * @throws DSMException
     */
    public JSONArray sendJSONRequestArray(String uriP, String requestP, String methodP) throws Exception {
        return sendJSONRequestArray(uriP, requestP, methodP, true);
    }

    /**
     * Send a request to the server.
     * 
     * @param uriP
     *            The part of the URI ie: /webman/doit.cgi
     * @param requestP
     *            The query in the form 'param1=foo&param2=yes'
     * @param methodP
     *            The method to send this request
     * @return A JSONObject containing the response of the server
     * @throws DSMException
     */
    public JSONArray sendJSONRequestArray(String uriP, String requestP, String methodP, boolean log)
            throws Exception {
        HttpURLConnection con = null;
        OutputStreamWriter wr = null;
        BufferedReader br = null;
        StringBuffer sb = null;
        Exception last_exception = null;
        try {

            // For some reason in Gingerbread I often get a response code of -1.
            // Here I retry for a maximum of MAX_RETRY to send the request and it usually succeed at the second try...
            int retry = 0;
            int MAX_RETRY = 2;
            while (retry <= MAX_RETRY) {
                try {
                    // Create the connection
                    con = createConnection(uriP, requestP, methodP, log);
                    // Add the parameters
                    wr = new OutputStreamWriter(con.getOutputStream());
                    wr.write(requestP);
                    // Send the request
                    wr.flush();
                    wr.close();

                    // Try to retrieve the session cookie
                    String newCookie = con.getHeaderField("set-cookie");
                    if (newCookie != null) {
                        synchronized (this) {
                            setCookie(newCookie);
                        }
                        if (DEBUG)
                            Log.v(Synodroid.DS_TAG, "Retreived cookies: " + cookies);
                    }

                    // Now read the reponse and build a string with it
                    br = new BufferedReader(new InputStreamReader(con.getInputStream()));
                    sb = new StringBuffer();
                    String line;
                    while ((line = br.readLine()) != null) {
                        sb.append(line);
                    }
                    br.close();
                    // Verify is response if not -1, otherwise take reason from the header
                    if (con.getResponseCode() == -1) {
                        retry++;
                        if (DEBUG)
                            Log.w(Synodroid.DS_TAG, "Response code is -1 (retry: " + retry + ")");
                    } else {
                        if (DEBUG)
                            Log.d(Synodroid.DS_TAG, "Response is: " + sb.toString());
                        JSONArray respJSO = null;
                        try {
                            respJSO = new JSONArray(sb.toString());
                        } catch (JSONException je) {
                            respJSO = new JSONArray();
                        }
                        return respJSO;
                    }
                } catch (EOFException e) {
                    if (DEBUG)
                        Log.w(Synodroid.DS_TAG, "Caught EOFException while contacting the server, retying...");
                    retry++;
                    last_exception = e;
                } catch (SocketException e) {
                    if (DEBUG)
                        Log.e(Synodroid.DS_TAG, "Caught SocketException while contacting the server, stopping...");
                    throw e;
                } catch (SSLHandshakeException e) {
                    if (DEBUG)
                        Log.e(Synodroid.DS_TAG,
                                "Caught SSLHandshakeException while contacting the server, stopping...");
                    throw e;
                } catch (FileNotFoundException e) {
                    String msg = e.getMessage();
                    if (DEBUG)
                        Log.e(Synodroid.DS_TAG,
                                "Could not find file " + msg + "\nProbably wrong DSM version, stopping...");
                    throw e;
                } catch (Exception e) {
                    if (DEBUG)
                        Log.e(Synodroid.DS_TAG, "Caught exception while contacting the server, retying...", e);
                    retry++;
                    last_exception = e;
                } finally {
                    con.disconnect();
                }

            }
            if (last_exception != null)
                throw last_exception;
            throw new GenericException();
        } finally {
            wr = null;
            br = null;
            sb = null;
            con = null;
        }
    }

    public JSONObject sendJSONRequest(String uriP, String requestP, String methodP, boolean log) throws Exception {
        return sendJSONRequest(uriP, requestP, methodP, log, 2);
    }

    /**
     * Send a request to the server.
     * 
     * @param uriP
     *            The part of the URI ie: /webman/doit.cgi
     * @param requestP
     *            The query in the form 'param1=foo&param2=yes'
     * @param methodP
     *            The method to send this request
     * @return A JSONObject containing the response of the server
     * @throws DSMException
     */
    public JSONObject sendJSONRequest(String uriP, String requestP, String methodP, boolean log, int MAX_RETRY)
            throws Exception {
        HttpURLConnection con = null;
        OutputStreamWriter wr = null;
        BufferedReader br = null;
        StringBuffer sb = null;
        Exception last_exception = null;
        try {

            // For some reason in Gingerbread I often get a response code of -1.
            // Here I retry for a maximum of MAX_RETRY to send the request and it usually succeed at the second try...
            int retry = 0;
            while (retry <= MAX_RETRY) {
                try {
                    // Create the connection
                    con = createConnection(uriP, requestP, methodP, log);
                    // Add the parameters
                    wr = new OutputStreamWriter(con.getOutputStream());
                    wr.write(requestP);
                    // Send the request
                    wr.flush();
                    wr.close();

                    // Try to retrieve the session cookie
                    String newCookie = con.getHeaderField("set-cookie");
                    if (newCookie != null) {
                        synchronized (this) {
                            setCookie(newCookie);
                        }
                        if (DEBUG)
                            Log.v(Synodroid.DS_TAG, "Retreived cookies: " + cookies);
                    }

                    // Now read the reponse and build a string with it
                    br = new BufferedReader(new InputStreamReader(con.getInputStream()));
                    sb = new StringBuffer();
                    String line;
                    while ((line = br.readLine()) != null) {
                        sb.append(line);
                    }
                    br.close();
                    // Verify is response if not -1, otherwise take reason from the header
                    if (con.getResponseCode() == -1) {
                        retry++;
                        last_exception = null;
                        if (DEBUG)
                            Log.w(Synodroid.DS_TAG, "Response code is -1 (retry: " + retry + ")");
                    } else {
                        if (DEBUG)
                            Log.d(Synodroid.DS_TAG, "Response is: " + sb.toString());
                        JSONObject respJSO = null;
                        try {
                            respJSO = new JSONObject(sb.toString());
                        } catch (JSONException je) {
                            respJSO = new JSONObject();
                        }
                        return respJSO;
                    }
                } catch (EOFException e) {
                    if (DEBUG)
                        Log.w(Synodroid.DS_TAG, "Caught EOFException while contacting the server, retying...");
                    retry++;
                    last_exception = e;
                } catch (SocketException e) {
                    if (DEBUG)
                        Log.e(Synodroid.DS_TAG, "Caught SocketException while contacting the server, stopping...");
                    throw e;
                } catch (SSLHandshakeException e) {
                    if (DEBUG)
                        Log.e(Synodroid.DS_TAG,
                                "Caught SSLHandshakeException while contacting the server, stopping...");
                    throw e;
                } catch (FileNotFoundException e) {
                    String msg = e.getMessage();
                    if (DEBUG)
                        Log.e(Synodroid.DS_TAG,
                                "Could not find file " + msg + "\nProbably wrong DSM version, stopping...");
                    throw e;
                } catch (Exception e) {
                    if (DEBUG)
                        Log.e(Synodroid.DS_TAG, "Caught exception while contacting the server, retying...", e);
                    retry++;
                    last_exception = e;
                } finally {
                    con.disconnect();
                }

            }
            if (last_exception != null)
                throw last_exception;
            throw new GenericException();
        }
        // Finally close everything
        finally {
            wr = null;
            br = null;
            sb = null;
            con = null;
        }
    }

    /**
     * Upload a file which is located on the mobile
     */
    public JSONObject sendMultiPart(String uriP, MultipartBuilder multiPartP) throws Exception {
        HttpURLConnection conn = null;
        JSONObject respJSO = null;
        int retry = 0;
        int MAX_RETRY = 2;
        Exception last_exception = null;
        try {
            while (retry <= MAX_RETRY) {
                try {
                    // Create the connection
                    conn = createConnection(uriP, "", "POST", true);
                    conn.setRequestProperty("Connection", "keep-alive");
                    conn.setRequestProperty("Content-Type",
                            "multipart/form-data; boundary=" + multiPartP.getBoundary());

                    // Write the multipart
                    multiPartP.writeData(conn.getOutputStream());

                    // Now read the reponse and build a string with it
                    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                    StringBuffer sb = new StringBuffer();
                    String line;
                    while ((line = br.readLine()) != null) {
                        sb.append(line);
                    }
                    br.close();

                    if (conn.getResponseCode() == -1) {
                        retry++;
                        if (DEBUG)
                            Log.w(Synodroid.DS_TAG, "Response code is -1 (retry: " + retry + ")");
                    } else {
                        if (DEBUG)
                            Log.d(Synodroid.DS_TAG, "Response is: " + sb.toString());
                        respJSO = new JSONObject(sb.toString());
                        return respJSO;
                    }
                } catch (EOFException e) {
                    if (DEBUG)
                        Log.w(Synodroid.DS_TAG, "Caught EOFException while contacting the server, retying...");
                    retry++;
                    last_exception = e;
                } catch (SocketException e) {
                    if (DEBUG)
                        Log.e(Synodroid.DS_TAG, "Caught SocketException while contacting the server, stopping...");
                    throw e;
                } catch (SSLHandshakeException e) {
                    if (DEBUG)
                        Log.e(Synodroid.DS_TAG,
                                "Caught SSLHandshakeException while contacting the server, stopping...");
                    throw e;
                } catch (FileNotFoundException e) {
                    String msg = e.getMessage();
                    if (DEBUG)
                        Log.e(Synodroid.DS_TAG,
                                "Could not find file " + msg + "\nProbably wrong DSM version, stopping...");
                    throw e;
                } catch (Exception e) {
                    if (DEBUG)
                        Log.e(Synodroid.DS_TAG, "Caught exception while contacting the server, retying...", e);
                    retry++;
                    last_exception = e;
                } finally {
                    conn.disconnect();
                }
            }
        } finally {
            conn = null;
        }
        if (last_exception != null)
            throw last_exception;
        throw new GenericException();
    }

    /**
     * @return the connected
     */
    public boolean isConnected() {
        return true;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((url == null) ? 0 : url.hashCode());
        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        SimpleSynoServer other = (SimpleSynoServer) obj;
        if (url == null) {
            if (other.url != null)
                return false;
        } else if (!url.equals(other.url))
            return false;
        return true;
    }

    ////NULLIFYING FUNCTIONS ********************************************//////////////
    public String getUser() {
        return "user";
    }

    public StringBuffer download(String uri, String path) throws Exception {
        return new StringBuffer();
    }

    public void setConnected(boolean nil) {
    }

    public void setDsmVersion(DSMVersion dsm, boolean nil) {
    }

    public String getPassword() {
        return "password";
    }

}