org.transdroid.daemon.Tfb4rt.Tfb4rtAdapter.java Source code

Java tutorial

Introduction

Here is the source code for org.transdroid.daemon.Tfb4rt.Tfb4rtAdapter.java

Source

/*
 *   This file is part of Transdroid <http://www.transdroid.org>
 *   
 *   Transdroid 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.
 *   
 *   Transdroid 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 Transdroid.  If not, see <http://www.gnu.org/licenses/>.
 *   
 */
package org.transdroid.daemon.Tfb4rt;

import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.URI;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonSettings;
import org.transdroid.daemon.IDaemonAdapter;
import org.transdroid.daemon.Torrent;
import org.transdroid.daemon.DaemonException.ExceptionType;
import org.transdroid.daemon.task.AddByFileTask;
import org.transdroid.daemon.task.AddByUrlTask;
import org.transdroid.daemon.task.DaemonTask;
import org.transdroid.daemon.task.DaemonTaskFailureResult;
import org.transdroid.daemon.task.DaemonTaskResult;
import org.transdroid.daemon.task.DaemonTaskSuccessResult;
import org.transdroid.daemon.task.RemoveTask;
import org.transdroid.daemon.task.RetrieveTask;
import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.util.DLog;
import org.transdroid.daemon.util.HttpHelper;
import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part;

/**
 * An adapter that allows for easy access to Torrentflux-b4rt installs. Communication is handled via HTTP GET requests
 * and XML responses.
 * @author erickok
 */
public class Tfb4rtAdapter implements IDaemonAdapter {

    private static final String LOG_NAME = "Torrentflux-b4rt daemon";

    private static final String RPC_URL_STATS = "/stats.php?t=all&f=xml";
    private static final String RPC_URL_DISPATCH = "/dispatcher.php?action=";
    private static final String RPC_URL_DISPATCH2 = "&riid=_exit_";
    private static final String RPC_URL_TRANSFER = "&transfer=";
    private static final String RPC_URL_AID = "&aid=2";
    private static final String RPC_URL_URL = "&url=";
    private static final String RPC_URL_USER = "&username=";
    private static final String RPC_URL_PASS = "&md5pass=";

    private DaemonSettings settings;
    private DefaultHttpClient httpclient;

    /**
     * Initialises an adapter that provides operations to the Torrentflux-b4rt pages
     */
    public Tfb4rtAdapter(DaemonSettings settings) {
        this.settings = settings;
    }

    @Override
    public DaemonTaskResult executeTask(DaemonTask task) {

        try {
            switch (task.getMethod()) {
            case Retrieve:

                // Request all torrents from server
                return new RetrieveTaskSuccessResult((RetrieveTask) task, makeStatsRequest(), null);

            case AddByFile:

                // Add a torrent to the server by sending the contents of a local .torrent file
                String file = ((AddByFileTask) task).getFile();
                makeFileUploadRequest("fileUpload", file);
                return null;

            case AddByUrl:

                // Request to add a torrent by URL
                String url = ((AddByUrlTask) task).getUrl();
                makeActionRequest("urlUpload", url);
                return new DaemonTaskSuccessResult(task);

            case Remove:

                // Remove a torrent
                RemoveTask removeTask = (RemoveTask) task;
                makeActionRequest((removeTask.includingData() ? "deleteWithData" : "delete"),
                        task.getTargetTorrent().getUniqueID());
                return new DaemonTaskSuccessResult(task);

            case Pause:

                // Pause a torrent
                makeActionRequest("stop", task.getTargetTorrent().getUniqueID());
                return new DaemonTaskSuccessResult(task);

            case PauseAll:

                // Pause all torrents
                makeActionRequest("bulkStop", null);
                return new DaemonTaskSuccessResult(task);

            case Resume:

                // Resume a torrent
                makeActionRequest("start", task.getTargetTorrent().getUniqueID());
                return new DaemonTaskSuccessResult(task);

            case ResumeAll:

                // Resume all torrents
                makeActionRequest("bulkStart", null);
                return new DaemonTaskSuccessResult(task);

            case SetTransferRates:

                // Request to set the maximum transfer rates
                // TODO: Implement this?
                return null;

            default:
                return new DaemonTaskFailureResult(task, new DaemonException(ExceptionType.MethodUnsupported,
                        task.getMethod() + " is not supported by " + getType()));
            }
        } catch (DaemonException e) {
            return new DaemonTaskFailureResult(task, e);
        }
    }

    private List<Torrent> makeStatsRequest() throws DaemonException {

        try {

            // Initialise the HTTP client
            if (httpclient == null) {
                initialise();
            }

            // Make request
            HttpGet httpget = new HttpGet(buildWebUIUrl(RPC_URL_STATS));
            HttpResponse response = httpclient.execute(httpget);

            // Read XML response
            InputStream instream = response.getEntity().getContent();
            List<Torrent> torrents = StatsParser.parse(new InputStreamReader(instream));
            instream.close();
            return torrents;

        } catch (DaemonException e) {
            DLog.d(LOG_NAME, "Parsing error: " + e.toString());
            throw e;
        } catch (Exception e) {
            DLog.d(LOG_NAME, "Error: " + e.toString());
            throw new DaemonException(ExceptionType.ConnectionError, e.toString());
        }

    }

    private boolean makeActionRequest(String action, String target) throws DaemonException {

        try {

            // Initialise the HTTP client
            if (httpclient == null) {
                initialise();
            }

            // Make request
            HttpGet httpget = new HttpGet(buildWebUIUrl(RPC_URL_DISPATCH + action + RPC_URL_DISPATCH2 + RPC_URL_AID
                    + (action.equals("urlUpload") ? RPC_URL_URL : RPC_URL_TRANSFER) + target));
            HttpResponse response = httpclient.execute(httpget);

            // Read response (a successful action always returned '1')
            InputStream instream = response.getEntity().getContent();
            String result = HttpHelper.convertStreamToString(instream);
            instream.close();
            if (result.trim().equals("1")) {
                return true;
            } else {
                throw new DaemonException(ExceptionType.UnexpectedResponse,
                        "Action response was not 1 but " + result);
            }

        } catch (DaemonException e) {
            DLog.d(LOG_NAME, action + " request error: " + e.toString());
            throw e;
        } catch (Exception e) {
            DLog.d(LOG_NAME, "Error: " + e.toString());
            throw new DaemonException(ExceptionType.ConnectionError, e.toString());
        }

    }

    private boolean makeFileUploadRequest(String action, String target) throws DaemonException {

        try {

            // Initialise the HTTP client
            if (httpclient == null) {
                initialise();
            }

            // Make request
            HttpPost httppost = new HttpPost(
                    buildWebUIUrl(RPC_URL_DISPATCH + action + RPC_URL_DISPATCH2 + RPC_URL_AID));

            File upload = new File(URI.create(target));
            Part[] parts = { new FilePart("upload_files[]", upload) };
            httppost.setEntity(new MultipartEntity(parts, httppost.getParams()));
            HttpResponse response = httpclient.execute(httppost);

            // Read response (a successful action always returned '1')
            InputStream instream = response.getEntity().getContent();
            String result = HttpHelper.convertStreamToString(instream);
            instream.close();
            if (result.equals("1")) {
                return true;
            } else {
                throw new DaemonException(ExceptionType.UnexpectedResponse,
                        "Action response was not 1 but " + result);
            }

        } catch (DaemonException e) {
            DLog.d(LOG_NAME, action + " request error: " + e.toString());
            throw e;
        } catch (Exception e) {
            DLog.d(LOG_NAME, "Error: " + e.toString());
            throw new DaemonException(ExceptionType.ConnectionError, e.toString());
        }

    }

    /**
     * Instantiates an HTTP client that can be used for all Torrentflux-b4rt requests.
     * @param connectionTimeout The connection timeout in milliseconds
     * @throws DaemonException On conflicting or missing settings
     */
    private void initialise() throws DaemonException {
        httpclient = HttpHelper.createStandardHttpClient(settings, true);
    }

    /**
     * Build the URL of specific Torrentflux site request from the user settings and some requested action.
     * @param act The action to perform, which is an already build query string without usernmae/password, i.e.
     *            dispatcher.php?action=stop&transfer=ubuntu.torrent
     * @return The URL of a specific request, i.e.
     *         http://localhost:80/turrentflux/dispatcher.php?action=stop&transfer=ubuntu
     *         .torrent&username=admin&md5pass=asd98as7d
     */
    private String buildWebUIUrl(String act) {
        String folder = "";
        if (settings.getFolder() != null) {
            folder = settings.getFolder();
            if (folder.endsWith("/"))
                folder = folder.substring(0, folder.length() - 1);
        }
        return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort()
                + folder + act + RPC_URL_USER + settings.getUsername() + RPC_URL_PASS
                + md5Pass((settings.getPassword() == null ? "" : settings.getPassword()));
    }

    /**
     * Calculate the MD5 hash of a password to use with the Torrentflux-b4rt dispatcher requests.
     * @param pass The plain text password
     * @return A hex-formatted MD5-hashed string of the password
     */
    public static String md5Pass(String pass) {
        try {
            MessageDigest m = MessageDigest.getInstance("MD5");
            byte[] data = pass.getBytes();
            m.update(data, 0, data.length);
            BigInteger i = new BigInteger(1, m.digest());
            return String.format("%1$032X", i);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Daemon getType() {
        return settings.getType();
    }

    @Override
    public DaemonSettings getSettings() {
        return this.settings;
    }

}