org.transdroid.daemon.DLinkRouterBT.DLinkRouterBTAdapter.java Source code

Java tutorial

Introduction

Here is the source code for org.transdroid.daemon.DLinkRouterBT.DLinkRouterBTAdapter.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.DLinkRouterBT;

import com.android.internalcopy.http.multipart.FilePart;
import com.android.internalcopy.http.multipart.MultipartEntity;
import com.android.internalcopy.http.multipart.Part;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.transdroid.core.gui.log.Log;
import org.transdroid.daemon.Daemon;
import org.transdroid.daemon.DaemonException;
import org.transdroid.daemon.DaemonException.ExceptionType;
import org.transdroid.daemon.DaemonSettings;
import org.transdroid.daemon.IDaemonAdapter;
import org.transdroid.daemon.Priority;
import org.transdroid.daemon.Torrent;
import org.transdroid.daemon.TorrentFile;
import org.transdroid.daemon.TorrentStatus;
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.GetFileListTask;
import org.transdroid.daemon.task.GetFileListTaskSuccessResult;
import org.transdroid.daemon.task.PauseTask;
import org.transdroid.daemon.task.RemoveTask;
import org.transdroid.daemon.task.ResumeTask;
import org.transdroid.daemon.task.RetrieveTask;
import org.transdroid.daemon.task.RetrieveTaskSuccessResult;
import org.transdroid.daemon.util.HttpHelper;

import java.io.File;
import java.net.URI;
import java.util.ArrayList;

/**
 * The daemon adapter for the DLink Router Bittorrent client.
 * @author AvengerMoJo <avengermojo at gmail.com>
 */
public class DLinkRouterBTAdapter implements IDaemonAdapter {

    private static final String LOG_NAME = "DLinkRouterBT adapter";

    private static final String PATH_TO_API = "/api/";
    private static final String SESSION_HEADER = "X-Session-Id";
    private static final String JSON_TORRENTS = "torrents";

    private static final String API_GET = "torrents-get";
    private static final String API_ADD = "torrent-add-url?start=yes&url=";
    private static final String API_ADD_BY_FILE = "torrent-add?start=yes";
    private static final String API_REMOVE = "torrent-remove?delete-torrent=yes&hash=";
    private static final String API_DEL_DATA = "&delete-data=";
    private static final String API_STOP = "torrent-stop?hash=";
    private static final String API_START = "torrent-start?hash=";
    private static final String BT_ADD_BY_FILE = "fileEl";

    private static final String BT_CAPTION = "caption";
    private static final String BT_COPYS = "distributed_copies";
    private static final String BT_DOWNLOAD_RATE = "dl_rate";
    private static final String BT_DONE = "done";
    private static final String BT_HASH = "hash";
    // private static final String BT_MAX_CONNECTED = "max_connections";
    // private static final String BT_MAX_DOWNLOAD_RATE = "max_dl_rate";
    // private static final String BT_MAX_UPLOAD_RATE = "max_ul_rate";
    // private static final String BT_MAX_UPLOAD_CONNECTIONS = "max_uploads";
    // private static final String BT_PAYLOAD_DOWNLOAD = "payload_download";
    private static final String BT_PAYLOAD_UPLOAD = "payload_upload";
    private static final String BT_PEERS_CONNECTED = "peers_connected";
    private static final String BT_PEERS_TOTAL = "peers_total";
    // private static final String BT_PRIVATE = "private";
    private static final String BT_SEEDS_CONNECTED = "seeds_connected";
    private static final String BT_SEEDS_TOTAL = "seeds_total";
    private static final String BT_SIZE = "size";
    private static final String BT_STATE = "state";
    private static final String BT_STOPPED = "stopped";
    private static final String BT_UPLOAD_RATE = "ul_rate";

    private static final String API_GET_FILES = "torrent-get-files?hash=";
    private static final String BT_FILE_DONE = "done";
    private static final String BT_FILE_NAME = "name";
    private static final String BT_FILE_SIZE = "size";
    private static final String BT_FILE_PRIORITY = "pri";

    private DaemonSettings settings;
    private DefaultHttpClient httpclient;
    private String sessionToken;

    public DLinkRouterBTAdapter(DaemonSettings settings) {
        this.settings = settings;
    }

    @Override
    public DaemonTaskResult executeTask(Log log, DaemonTask task) {

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

                // Request all torrents from server
                JSONObject result = makeRequest(log, API_GET);
                return new RetrieveTaskSuccessResult((RetrieveTask) task, parseJsonRetrieveTorrents(result), null);

            case GetFileList:

                // Request all details for a specific torrent
                JSONObject result2 = makeRequest(log, API_GET_FILES + task.getTargetTorrent().getUniqueID());
                return new GetFileListTaskSuccessResult((GetFileListTask) task,
                        parseJsonFileList(result2, task.getTargetTorrent().getUniqueID()));

            case AddByFile:

                // Add a torrent to the server by sending the contents of a local .torrent file
                String file = ((AddByFileTask) task).getFile();

                // put .torrent file's data into the request
                makeRequest(log, API_ADD_BY_FILE, new File(URI.create(file)));
                return new DaemonTaskSuccessResult(task);

            case AddByUrl:

                // Request to add a torrent by URL
                String url = ((AddByUrlTask) task).getUrl();
                makeRequest(log, API_ADD + url);
                return new DaemonTaskSuccessResult(task);

            case Remove:

                // Remove a torrent
                RemoveTask removeTask = (RemoveTask) task;
                makeRequest(log, API_REMOVE + removeTask.getTargetTorrent().getUniqueID()
                        + (removeTask.includingData() ? API_DEL_DATA + "yes" : ""), false);
                return new DaemonTaskSuccessResult(task);

            // case Stop:
            case Pause:

                // Pause a torrent
                PauseTask pauseTask = (PauseTask) task;
                makeRequest(log, API_STOP + pauseTask.getTargetTorrent().getUniqueID(), false);
                return new DaemonTaskSuccessResult(task);

            // case PauseAll:

            // Resume all torrents
            // makeRequest(buildRequestObject(RPC_METHOD_PAUSE, buildTorrentRequestObject(FOR_ALL, null,
            // false)));
            // return new DaemonTaskSuccessResult(task);

            // case Start:
            case Resume:

                // Resume a torrent
                ResumeTask resumeTask = (ResumeTask) task;
                makeRequest(log, API_START + resumeTask.getTargetTorrent().getUniqueID(), false);
                return new DaemonTaskSuccessResult(task);

            // case ResumeAll:

            // Resume all torrents
            // makeRequest(buildRequestObject(RPC_METHOD_RESUME, buildTorrentRequestObject(FOR_ALL, null,
            // false)));
            // return new DaemonTaskSuccessResult(task);

            // case SetTransferRates:

            // Request to set the maximum transfer rates
            // SetTransferRatesTask ratesTask = (SetTransferRatesTask) task;
            // if (ratesTask.getUploadRate() == null) {
            // request.put(RPC_SESSION_LIMITUPE, false);
            // } else {
            // request.put(RPC_SESSION_LIMITUPE, true);
            // request.put(RPC_SESSION_LIMITUP, ratesTask.getUploadRate().intValue());
            // }
            // if (ratesTask.getDownloadRate() == null) {
            // request.put(RPC_SESSION_LIMITDOWNE, false);
            // } else {
            // request.put(RPC_SESSION_LIMITDOWNE, true);
            // request.put(RPC_SESSION_LIMITDOWN, ratesTask.getDownloadRate().intValue());
            // }

            // makeRequest( RPC_METHOD_SESSIONSET );
            // return new DaemonTaskSuccessResult(task);

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

    private JSONObject makeRequest(Log log, String requestUrl, File upload) throws DaemonException {
        return makeRequest(log, requestUrl, false, upload);
    }

    private JSONObject makeRequest(Log log, String requestUrl) throws DaemonException {
        return makeRequest(log, requestUrl, true, null);
    }

    private JSONObject makeRequest(Log log, String requestUrl, boolean hasRespond) throws DaemonException {
        return makeRequest(log, requestUrl, hasRespond, null);
    }

    private JSONObject makeRequest(Log log, String requestUrl, boolean hasRespond, File upload)
            throws DaemonException {

        try {

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

            // Setup request using POST stream with URL and data
            HttpPost httppost = new HttpPost(buildWebUIUrl() + requestUrl);
            if (upload != null) {
                Part[] parts = { new FilePart(BT_ADD_BY_FILE, upload) };
                httppost.setEntity(new MultipartEntity(parts, httppost.getParams()));
            }

            // Send the stored session token as a header
            if (sessionToken != null) {
                httppost.addHeader(SESSION_HEADER, sessionToken);
            }

            // Execute
            HttpResponse response = httpclient.execute(httppost);

            // 409 error because of a session id?
            if (response.getStatusLine().getStatusCode() == 409) {

                // Retry post, but this time with the new session token that was encapsulated in the 409
                // response
                sessionToken = response.getFirstHeader(SESSION_HEADER).getValue();
                httppost.addHeader(SESSION_HEADER, sessionToken);
                response = httpclient.execute(httppost);

            }
            if (!hasRespond) {
                return null;
            }

            HttpEntity entity = response.getEntity();
            if (entity != null) {

                // Read JSON response
                java.io.InputStream instream = entity.getContent();
                String result = HttpHelper.convertStreamToString(instream);
                JSONObject json = new JSONObject(result);
                instream.close();

                log.d(LOG_NAME,
                        "Success: " + (result.length() > 300
                                ? result.substring(0, 300) + "... (" + result.length() + " chars)"
                                : result));

                // Return the JSON object
                return json;
            }

            log.d(LOG_NAME, "Error: No entity in HTTP response");
            throw new DaemonException(ExceptionType.UnexpectedResponse, "No HTTP entity object in response.");

        } catch (DaemonException e) {
            throw e;
        } catch (JSONException e) {
            log.d(LOG_NAME, "Error: " + e.toString());
            throw new DaemonException(ExceptionType.UnexpectedResponse, e.toString());
        } catch (Exception e) {
            log.d(LOG_NAME, "Error: " + e.toString());
            throw new DaemonException(ExceptionType.ConnectionError, e.toString());
        }

    }

    /**
     * Instantiates an HTTP client with proper credentials that can be used for all Transmission requests.
     * @throws DaemonException On conflicting or missing settings
     */
    private void initialise() throws DaemonException {
        httpclient = HttpHelper.createStandardHttpClient(settings, true);
    }

    /**
     * Build the URL of the Transmission web UI from the user settings.
     * @return The URL of the RPC API
     */
    private String buildWebUIUrl() {
        return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort()
                + PATH_TO_API;
    }

    private TorrentStatus convertStatus(String state) {
        if ("allocating".equals(state)) {
            return TorrentStatus.Checking;
        }
        if ("seeding".equals(state)) {
            return TorrentStatus.Seeding;
        }
        if ("finished".equals(state)) {
            return TorrentStatus.Downloading;
        }
        if ("connecting_to_tracker".equals(state)) {
            return TorrentStatus.Checking;
        }
        if ("queued_for_checking".equals(state)) {
            return TorrentStatus.Queued;
        }
        if ("downloading".equals(state)) {
            return TorrentStatus.Downloading;
        }
        return TorrentStatus.Unknown;
    }

    private ArrayList<Torrent> parseJsonRetrieveTorrents(JSONObject response) throws JSONException {

        // Parse response
        ArrayList<Torrent> torrents = new ArrayList<Torrent>();
        JSONArray rarray = response.getJSONArray(JSON_TORRENTS);
        for (int i = 0; i < rarray.length(); i++) {
            JSONObject tor = rarray.getJSONObject(i);
            // Add the parsed torrent to the list
            TorrentStatus status;
            if (tor.getInt(BT_STOPPED) == 1) {
                status = TorrentStatus.Paused;
            } else {
                status = convertStatus(tor.getString(BT_STATE));
            }
            int eta = (int) ((tor.getLong(BT_SIZE) - tor.getLong(BT_DONE)) / (tor.getInt(BT_DOWNLOAD_RATE) + 1));
            if (0 > eta) {
                eta = -1;
            }

            // @formatter:off
            Torrent new_t = new Torrent(i, tor.getString(BT_HASH), tor.getString(BT_CAPTION), status, null, // Not supported?
                    tor.getInt(BT_DOWNLOAD_RATE), tor.getInt(BT_UPLOAD_RATE), tor.getInt(BT_PEERS_CONNECTED),
                    tor.getInt(BT_PEERS_TOTAL), tor.getInt(BT_SEEDS_CONNECTED), tor.getInt(BT_SEEDS_TOTAL), eta,
                    tor.getLong(BT_DONE), tor.getLong(BT_PAYLOAD_UPLOAD), tor.getLong(BT_SIZE),
                    tor.getLong(BT_DONE) / (float) tor.getLong(BT_SIZE), Float.parseFloat(tor.getString(BT_COPYS)),
                    null, null, null, null, settings.getType());
            // @formatter:on

            torrents.add(new_t);
        }

        // Return the list
        return torrents;

    }

    private ArrayList<TorrentFile> parseJsonFileList(JSONObject response, String hash) throws JSONException {

        // Parse response
        ArrayList<TorrentFile> torrentfiles = new ArrayList<TorrentFile>();
        JSONObject jobj = response.getJSONObject(JSON_TORRENTS);
        if (jobj != null) {
            JSONArray files = jobj.getJSONArray(hash); // "Hash id"
            for (int i = 0; i < files.length(); i++) {
                JSONObject file = files.getJSONObject(i);
                // @formatter:off
                torrentfiles.add(new TorrentFile(String.valueOf(i), file.getString(BT_FILE_NAME),
                        file.getString(BT_FILE_NAME), null, // Not supported?
                        file.getLong(BT_FILE_SIZE), file.getLong(BT_FILE_DONE),
                        convertTransmissionPriority(file.getInt(BT_FILE_PRIORITY))));
                // @formatter:on
            }
        }

        // Return the list
        return torrentfiles;

    }

    private Priority convertTransmissionPriority(int priority) {
        switch (priority) {
        case 1:
            return Priority.High;
        case -1:
            return Priority.Low;
        default:
            return Priority.Normal;
        }
    }

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

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

}