package com.tarpha.torrssen2.service; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.List; import java.util.Optional; import com.tarpha.torrssen2.domain.DownloadList; import com.tarpha.torrssen2.repository.DownloadListRepository; import org.apache.commons.lang3.StringUtils; import org.apache.http.Header; import org.apache.http.HttpHeaders; import org.apache.http.ParseException; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.HttpClientUtils; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicHeader; import org.apache.http.util.EntityUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @Service @Slf4j public class TransmissionService { @Autowired private DownloadListRepository downloadListRepository; @Autowired private SettingService settingService; @Autowired private CryptoService cryptoService; // @Value("${transmission.username}") private String username; // @Value("${transmission.password}") private String password; // @Value("http://${transmission.host}:${transmission.port}/transmission/rpc") private String baseUrl; private CloseableHttpClient httpClient = null; private String xTransmissionSessionId = null; @Getter @Setter private class TransmissionVO { private String result; private JSONObject arguments; } public boolean initialize() { username = settingService.getSettingValue("TRANSMISSION_USERNAME"); try { password = cryptoService.decrypt(settingService.getSettingValue("TRANSMISSION_PASSWORD")); } catch (UnsupportedEncodingException | GeneralSecurityException e) { log.error(e.getMessage()); } baseUrl = "http://" + settingService.getSettingValue("TRANSMISSION_HOST") + ":" + settingService.getSettingValue("TRANSMISSION_PORT") + "/transmission/rpc"; CredentialsProvider provider = new BasicCredentialsProvider(); UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password); provider.setCredentials(AuthScope.ANY, credentials); if (StringUtils.isEmpty(xTransmissionSessionId)) { CloseableHttpResponse response = null; try { httpClient = HttpClientBuilder.create().setDefaultCredentialsProvider(provider).build(); response = httpClient.execute(new HttpGet(baseUrl)); xTransmissionSessionId = response.getFirstHeader("X-Transmission-Session-Id").getValue(); } catch (IOException e) { log.error(e.getMessage()); } finally { HttpClientUtils.closeQuietly(response); HttpClientUtils.closeQuietly(httpClient); } } List<Header> headers = new ArrayList<Header>(); headers.add(new BasicHeader(HttpHeaders.CONTENT_TYPE, "application/json")); headers.add(new BasicHeader("X-Transmission-Session-Id", xTransmissionSessionId)); httpClient = HttpClientBuilder.create().setDefaultCredentialsProvider(provider).setDefaultHeaders(headers) .build(); if(StringUtils.isEmpty(xTransmissionSessionId)) { return false; } return true; } public boolean test(String host, String port, String id, String pwd) { boolean ret = false; String url = "http://" + host + ":" + port + "/transmission/rpc"; if(StringUtils.equals(settingService.getSettingValue("TRANSMISSION_PASSWORD"), pwd)) { try { pwd = cryptoService.decrypt(pwd); } catch (UnsupportedEncodingException | GeneralSecurityException e) { log.error(e.getMessage()); } } CredentialsProvider provider = new BasicCredentialsProvider(); UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(id, pwd); provider.setCredentials(AuthScope.ANY, credentials); CloseableHttpResponse response = null; try { httpClient = HttpClientBuilder.create().setDefaultCredentialsProvider(provider).build(); response = httpClient.execute(new HttpGet(url)); if(response.getFirstHeader("X-Transmission-Session-Id") != null) { if(!StringUtils.isEmpty(response.getFirstHeader("X-Transmission-Session-Id").getValue())) { ret = true; } } } catch (IOException e) { log.error(e.getMessage()); } finally { HttpClientUtils.closeQuietly(response); HttpClientUtils.closeQuietly(httpClient); } httpClient = null; return ret; } private TransmissionVO execute(JSONObject params) { TransmissionVO ret = null; if(httpClient == null) { initialize(); } HttpPost httpPost = new HttpPost(baseUrl); CloseableHttpResponse response = null; log.debug(params.toString()); try { httpPost.setEntity(new StringEntity(params.toString(), "UTF-8")); response = httpClient.execute(httpPost); if (response.getStatusLine().getStatusCode() == 409) { xTransmissionSessionId = response.getFirstHeader("X-Transmission-Session-Id").getValue(); response.close(); httpClient.close(); initialize(); response = httpClient.execute(httpPost); } log.debug("transmission-execute-response-code: " + response.getStatusLine().getStatusCode()); if (response.getStatusLine().getStatusCode() == 200) { JSONObject resJson = new JSONObject(EntityUtils.toString(response.getEntity())); log.debug(resJson.toString()); ret = new TransmissionVO(); if (resJson.has("result")) { ret.setResult(resJson.getString("result")); } if (resJson.has("arguments")) { ret.setArguments(resJson.getJSONObject("arguments")); } } } catch (IOException | ParseException | JSONException e) { log.error(e.getMessage()); HttpClientUtils.closeQuietly(response); HttpClientUtils.closeQuietly(httpClient); httpClient = null; } HttpClientUtils.closeQuietly(response); return ret; } public int torrentAdd(String filename, String downloadDir) { int ret = -1; JSONObject params = new JSONObject(); try { params.put("method", "torrent-add"); JSONObject args = new JSONObject(); args.put("filename", filename); if (!StringUtils.isEmpty(downloadDir)) { args.put("download-dir", downloadDir); } log.debug(args.toString()); params.put("arguments", args); TransmissionVO vo = execute(params); if (vo != null) { if(vo.getArguments().has("torrent-added")) { ret = vo.getArguments().getJSONObject("torrent-added").getInt("id"); } else if(vo.getArguments().has("torrent-duplicate")) { ret = -2; } } } catch (JSONException e) { log.error(e.getMessage()); } return ret; } public List<DownloadList> torrentGet(List<Long> ids) { List<DownloadList> ret = new ArrayList<DownloadList>(); JSONObject params = new JSONObject(); try { params.put("method", "torrent-get"); JSONObject args = new JSONObject(); // transmission.status = // STOPPED : 0 # Torrent is stopped // CHECK_WAIT : 1 # Queued to check files // CHECK : 2 # Checking files // DOWNLOAD_WAIT : 3 # Queued to download // DOWNLOAD : 4 # Downloading // SEED_WAIT : 5 # Queued to seed // SEED : 6 # Seeding // ISOLATED : 7 # Torrent can't find peers args.put("fields" , new JSONArray(new String[]{"id", "name", "totalSize", "percentDone", "status", "downloadDir", "isFinished", "magnetLink"})); if(ids != null && ids.size() > 0) { args.put("ids", new JSONArray(ids)); } log.debug(args.toString()); params.put("arguments", args); TransmissionVO vo = execute(params); if (vo != null) { if (vo.getArguments().has("torrents")) { for(int i = 0; i < vo.getArguments().getJSONArray("torrents").length(); i++) { JSONObject json = vo.getArguments().getJSONArray("torrents").getJSONObject(i); DownloadList down = new DownloadList(); down.setId(json.getLong("id")); down.setUri(json.getString("magnetLink")); down.setName(json.getString("name")); down.setDownloadPath(json.getString("downloadDir")); down.setPercentDone((int)(json.getDouble("percentDone") * 100)); down.setStatus(json.getInt("status")); down.setDone((boolean)json.get("isFinished") || json.getInt("status") == 6); Optional<DownloadList> info = downloadListRepository.findById(json.getLong("id")); if(info.isPresent()) { down.setVueItemIndex(info.get().getVueItemIndex()); } ret.add(down); } } } } catch (JSONException e) { log.error(e.getMessage()); } return ret; } public boolean torrentRemove(List<Long> ids) { boolean ret = true; JSONObject params = new JSONObject(); try { params.put("method", "torrent-remove"); JSONObject args = new JSONObject(); args.put("ids", new JSONArray(ids)); args.put("delete-local-data", false); log.debug(args.toString()); params.put("arguments", args); TransmissionVO vo = execute(params); if (vo != null) { if (!StringUtils.equals(vo.getResult(), "success")) { ret = false; } } } catch (JSONException e) { log.error(e.getMessage()); } return ret; } }