/* * 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.BuffaloNas; 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.HttpStatus; import org.apache.http.NameValuePair; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; 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.RemoveTask; import org.transdroid.daemon.task.RetrieveTask; import org.transdroid.daemon.task.RetrieveTaskSuccessResult; import org.transdroid.daemon.task.SetTransferRatesTask; import org.transdroid.daemon.util.HttpHelper; import java.io.File; import java.io.FileNotFoundException; import java.io.UnsupportedEncodingException; import java.net.URI; import java.util.ArrayList; /** * The daemon adapter for the Buffalo NAS' integrated torrent client. * @author erickok */ public class BuffaloNasAdapter implements IDaemonAdapter { private static final String LOG_NAME = "qBittorrent daemon"; private DaemonSettings settings; private DefaultHttpClient httpclient; public BuffaloNasAdapter(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 = new JSONObject(makeRequest(log, "/api/torrents-get")); return new RetrieveTaskSuccessResult((RetrieveTask) task, parseJsonTorrents(result), null); case GetFileList: // Request files listing for a specific torrent String fhash = task.getTargetTorrent().getUniqueID(); JSONObject files = new JSONObject( makeRequest(log, "/api/torrent-get-files", new BasicNameValuePair("hash", fhash))); return new GetFileListTaskSuccessResult((GetFileListTask) task, parseJsonFiles(files, fhash)); case AddByFile: // Upload a local .torrent file String ufile = ((AddByFileTask) task).getFile(); makeUploadRequest(log, "/api/torrent-add?start=yes", ufile); return new DaemonTaskSuccessResult(task); case AddByUrl: // Request to add a torrent by URL String url = ((AddByUrlTask) task).getUrl(); // @formatter:off makeRequest(log, "/api/torrent-add", new BasicNameValuePair("url", url), new BasicNameValuePair("start", "yes")); // @formatter:on return new DaemonTaskSuccessResult(task); case Remove: // Remove a torrent RemoveTask removeTask = (RemoveTask) task; // @formatter:off makeRequest(log, "/api/torrent-remove", new BasicNameValuePair("hash", removeTask.getTargetTorrent().getUniqueID()), new BasicNameValuePair("delete-torrent", "yes"), new BasicNameValuePair("delete-data", (removeTask.includingData() ? "yes" : "no"))); // @formatter:on return new DaemonTaskSuccessResult(task); case Pause: // Pause a torrent makeRequest(log, "/api/torrent-stop", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID())); return new DaemonTaskSuccessResult(task); case Resume: // Resume a torrent makeRequest(log, "/api/torrent-start", new BasicNameValuePair("hash", task.getTargetTorrent().getUniqueID())); return new DaemonTaskSuccessResult(task); case SetTransferRates: // Request to set the maximum transfer rates SetTransferRatesTask ratesTask = (SetTransferRatesTask) task; String dl = Integer.toString( (ratesTask.getDownloadRate() == null ? -1 : ratesTask.getDownloadRate() * 1024)); String ul = Integer.toString( (ratesTask.getUploadRate() == null ? -1 : ratesTask.getUploadRate() * 1024)); // @formatter:off makeRequest(log, "/api/app-settings-set", new BasicNameValuePair("auto_bandwidth_management", "0"), new BasicNameValuePair("max_dl_rate", dl), new BasicNameValuePair("max_ul_rate", ul), new BasicNameValuePair("max_ul_rate_seed", ul)); // @formatter:on 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 String makeRequest(Log log, String url, NameValuePair... params) throws DaemonException { try { // Initialise the HTTP client if (httpclient == null) { initialise(); } // Add the parameters to the query string boolean first = true; for (NameValuePair param : params) { if (first) { url += "?"; first = false; } else { url += "&"; } url += param.getName() + "=" + param.getValue(); } // Make the request HttpResponse response = httpclient.execute(new HttpGet(buildWebUIUrl(url))); HttpEntity entity = response.getEntity(); if (entity != null) { // Read JSON response java.io.InputStream instream = entity.getContent(); String result = HttpHelper.convertStreamToString(instream); instream.close(); // Return raw result return result; } log.d(LOG_NAME, "Error: No entity in HTTP response"); throw new DaemonException(ExceptionType.UnexpectedResponse, "No HTTP entity object in response."); } catch (UnsupportedEncodingException e) { throw new DaemonException(ExceptionType.ConnectionError, e.toString()); } catch (Exception e) { log.d(LOG_NAME, "Error: " + e.toString()); throw new DaemonException(ExceptionType.ConnectionError, e.toString()); } } private boolean makeUploadRequest(Log log, String path, String file) throws DaemonException { try { // Initialise the HTTP client if (httpclient == null) { initialise(); } // Setup request using POST HttpPost httppost = new HttpPost(buildWebUIUrl(path)); File upload = new File(URI.create(file)); Part[] parts = {new FilePart("fileEl", upload)}; httppost.setEntity(new MultipartEntity(parts, httppost.getParams())); // Make the request HttpResponse response = httpclient.execute(httppost); return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK; } catch (FileNotFoundException e) { throw new DaemonException(ExceptionType.FileAccessError, 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 Buffalo NAS requests. * @throws DaemonException On conflicting or missing settings */ private void initialise() throws DaemonException { httpclient = HttpHelper.createStandardHttpClient(settings, true); } /** * Build the URL of the http request from the user settings * @return The URL to request */ private String buildWebUIUrl(String path) { return (settings.getSsl() ? "https://" : "http://") + settings.getAddress() + ":" + settings.getPort() + path; } private ArrayList<Torrent> parseJsonTorrents(JSONObject response) throws JSONException { // Parse response ArrayList<Torrent> torrents = new ArrayList<Torrent>(); JSONArray all = response.getJSONArray("torrents"); for (int i = 0; i < all.length(); i++) { JSONObject tor = all.getJSONObject(i); int peersConnected = tor.getInt("peers_connected"); int seedsConnected = tor.getInt("seeds_connected"); int peersTotal = tor.getInt("peers_total"); int seedsTotal = tor.getInt("seeds_total"); long size = tor.getLong("size"); long sizeDone = tor.getLong("done"); long sizeUp = tor.getLong("payload_upload"); int rateUp = tor.getInt("dl_rate"); int rateDown = tor.getInt("ul_rate"); // Add the parsed torrent to the list // @formatter:off torrents.add(new Torrent( (long)i, tor.getString("hash"), tor.getString("caption"), parseStatus(tor.getString("state"), tor.getInt("stopped")), null, rateDown, rateUp, seedsConnected, seedsTotal, peersConnected, peersTotal, (rateDown == 0? -1: (int) ((size - sizeDone) / rateDown)), sizeDone, sizeUp, size, (float)sizeDone / size, (float)tor.getDouble("distributed_copies") / 10, null, null, null, null, settings.getType())); // @formatter:on } // Return the list return torrents; } private TorrentStatus parseStatus(String state, int stopped) { // Status is given as a descriptive string and an indication if the torrent was stopped/paused if (state.equals("downloading")) { if (stopped == 1) { return TorrentStatus.Paused; } else { return TorrentStatus.Downloading; } } else if (state.equals("seeding")) { if (stopped == 1) { return TorrentStatus.Paused; } else { return TorrentStatus.Seeding; } } return TorrentStatus.Unknown; } private ArrayList<TorrentFile> parseJsonFiles(JSONObject response, String hash) throws JSONException { // Parse response ArrayList<TorrentFile> torrentfiles = new ArrayList<TorrentFile>(); JSONArray all = response.getJSONObject("torrents").getJSONArray(hash); for (int i = 0; i < all.length(); i++) { JSONObject file = all.getJSONObject(i); long size = file.getLong("size"); long sizeDone = file.getLong("done"); // @formatter:off torrentfiles.add(new TorrentFile( "" + file.getInt("id"), file.getString("name"), file.getString("name"), settings.getDownloadDir() + file.getString("name"), size, sizeDone, Priority.Normal)); // @formatter:on } // Return the list return torrentfiles; } @Override public Daemon getType() { return settings.getType(); } @Override public DaemonSettings getSettings() { return this.settings; } }