package org.limewire.rest; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.apache.http.HttpEntity; import org.apache.http.HttpException; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.protocol.HttpContext; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.limewire.core.api.URN; import org.limewire.core.api.download.DownloadItem; import org.limewire.core.api.download.DownloadListManager; import org.limewire.core.api.library.URNFactory; import org.limewire.core.api.magnet.MagnetFactory; import org.limewire.core.api.magnet.MagnetLink; import org.limewire.core.api.search.GroupedSearchResult; import org.limewire.core.api.search.SearchManager; import org.limewire.core.api.search.SearchResultList; import org.limewire.io.GUID; import org.limewire.util.URIUtils; import com.google.inject.Inject; /** * Request handler for Download services. */ class DownloadRequestHandler extends AbstractRestRequestHandler { private static final String ALL = ""; private static final String START = ""; private final DownloadListManager downloadListManager; private final SearchManager searchManager; private final MagnetFactory magnetFactory; private final URNFactory urnFactory; @Inject public DownloadRequestHandler(DownloadListManager downloadListManager, SearchManager searchManager, MagnetFactory magnetFactory, URNFactory urnFactory) { this.downloadListManager = downloadListManager; this.searchManager = searchManager; this.magnetFactory = magnetFactory; this.urnFactory = urnFactory; } @Override public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException { // Get request method and uri target. String method = request.getRequestLine().getMethod(); String uriTarget = RestUtils.getUriTarget(request, RestPrefix.DOWNLOAD.pattern()); Map<String, String> queryParams = RestUtils.getQueryParams(request); if (RestUtils.GET.equals(method) && ALL.equals(uriTarget)) { // Get data for all downloads. doGetAll(uriTarget, queryParams, response); } else if (RestUtils.GET.equals(method) && (uriTarget.length() > 0)) { // Get progress for single download. doGetProgress(uriTarget, queryParams, response); } else if (RestUtils.POST.equals(method) && START.equals(uriTarget)) { // Start download. doStart(uriTarget, queryParams, response); } else if (RestUtils.DELETE.equals(method) && (uriTarget.length() > 0)) { // Cancel download. doCancel(uriTarget, response); } else { response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED); } } /** * Processes request to retrieve data for all downloads. */ private void doGetAll(String uriTarget, Map<String, String> queryParams, HttpResponse response) throws IOException { // Get list of downloads. List<DownloadItem> downloadItems = downloadListManager.getDownloads(); // Set response entity and status. updateDownloadResponse(response, downloadItems); } /** * Processes request to retrieve progress for a single download. */ private void doGetProgress(String uriTarget, Map<String, String> queryParams, HttpResponse response) throws IOException { // Create URN using ID string. String id = parseId(uriTarget); URN urn = urnFactory.createSHA1Urn(id); // Get download item. DownloadItem downloadItem = downloadListManager.getDownloadItem(urn); try { if (downloadItem != null) { // Return download data. JSONObject jsonObj = createDownloadDescription(downloadItem); HttpEntity entity = RestUtils.createStringEntity(jsonObj.toString()); response.setEntity(entity); response.setStatusCode(HttpStatus.SC_OK); } else { response.setStatusCode(HttpStatus.SC_NOT_FOUND); } } catch (JSONException ex) { throw new IOException(ex); } } /** * Starts a new download using the specified uri target and query parameters. */ private void doStart(String uriTarget, Map<String, String> queryParams, HttpResponse response) throws IOException { // Get parameters to start download. String fileId = queryParams.get("id"); String searchId = queryParams.get("searchId"); String magnet = queryParams.get("magnet"); String torrent = queryParams.get("torrent"); // Start download. List<DownloadItem> downloadItems = null; if ((fileId != null) && (searchId != null)) { downloadItems = startResultDownload(fileId, searchId); } else if (magnet != null) { downloadItems = startMagnetDownload(magnet); } else if (torrent != null) { downloadItems = startTorrentDownload(torrent); } // Return not found if download not started. if (downloadItems == null || downloadItems.isEmpty()) { response.setStatusCode(HttpStatus.SC_NOT_FOUND); return; } // Set response entity and status. updateDownloadResponse(response, downloadItems); } /** * Cancels a search using the specified uri target. */ private void doCancel(String uriTarget, HttpResponse response) throws IOException { // Create URN using ID string. String id = parseId(uriTarget); URN urn = urnFactory.createSHA1Urn(id); // Get download item. DownloadItem downloadItem = downloadListManager.getDownloadItem(urn); if (downloadItem != null) { if (downloadItem.getState().isFinished()) { // Remove finished download. downloadListManager.remove(downloadItem); } else { // Cancel existing download. downloadItem.cancel(); } // Set OK status. response.setStatusCode(HttpStatus.SC_OK); } else { response.setStatusCode(HttpStatus.SC_NOT_FOUND); } } /** * Starts a download of the specified search result and returns a list * containing the download item. The list is empty if the download could * not be started. */ private List<DownloadItem> startResultDownload(String fileUrn, String searchGuid) throws IOException { // Get search. SearchResultList resultList = searchManager.getSearchResultList(new GUID(searchGuid)); if (resultList == null) { return Collections.<DownloadItem>emptyList(); } // Find search results for URN. URN urn = urnFactory.createSHA1Urn(fileUrn); GroupedSearchResult groupedResult = resultList.getGroupedResult(urn); if (groupedResult == null) { return Collections.<DownloadItem>emptyList(); } // Add download to manager. DownloadItem downloadItem = downloadListManager.addDownload( resultList.getSearch(), groupedResult.getSearchResults(), null, true); return (downloadItem == null) ? Collections.<DownloadItem>emptyList() : Collections.<DownloadItem>singletonList(downloadItem); } /** * Starts a download of the specified magnet link and returns a list of * download items. The list is empty if the uri is not a magnet link. */ private List<DownloadItem> startMagnetDownload(String magnetUri) throws IOException { try { // Create URI. URI uri = URIUtils.toURI(magnetUri); if (magnetFactory.isMagnetLink(uri)) { // Create magnet links MagnetLink[] magnetLinks = magnetFactory.parseMagnetLink(uri); // Add download to manager. List<DownloadItem> downloadItems = new ArrayList<DownloadItem>(); for (int i = 0; i < magnetLinks.length; i++) { DownloadItem downloadItem = downloadListManager.addDownload(magnetLinks[i], null, true); if (downloadItem != null) downloadItems.add(downloadItem); } return downloadItems; } else { // Return empty list. return Collections.<DownloadItem>emptyList(); } } catch (URISyntaxException ex) { throw new IOException(ex); } } /** * Starts a download of the specified torrent uri and returns a list of * download items. The list is empty if the uri is really a magnet link. */ private List<DownloadItem> startTorrentDownload(String torrentUri) throws IOException { try { // Create URI. URI uri = URIUtils.toURI(torrentUri); if (!magnetFactory.isMagnetLink(uri)) { // Add download to manager. DownloadItem downloadItem = downloadListManager.addTorrentDownload(uri, true); return (downloadItem == null) ? Collections.<DownloadItem>emptyList() : Collections.<DownloadItem>singletonList(downloadItem); } else { // Return empty list. return Collections.<DownloadItem>emptyList(); } } catch (URISyntaxException ex) { throw new IOException(ex); } } /** * Creates the JSON description object for the specified download item. */ private JSONObject createDownloadDescription(DownloadItem downloadItem) throws JSONException { JSONObject jsonObj = new JSONObject(); jsonObj.put("filename", downloadItem.getFileName()); jsonObj.put("id", downloadItem.getUrn()); jsonObj.put("size", downloadItem.getTotalSize()); jsonObj.put("bytesDownloaded", downloadItem.getCurrentSize()); jsonObj.put("state", downloadItem.getState()); return jsonObj; } /** * Updates the HTTP response with the specified list of download items. */ private HttpResponse updateDownloadResponse(HttpResponse response, List<DownloadItem> downloadItems) throws IOException { try { // Create JSON result. JSONArray jsonArr = new JSONArray(); for (DownloadItem downloadItem : downloadItems) { jsonArr.put(createDownloadDescription(downloadItem)); } // Set response entity and status. HttpEntity entity = RestUtils.createStringEntity(jsonArr.toString()); response.setEntity(entity); response.setStatusCode(HttpStatus.SC_OK); return response; } catch (JSONException ex) { throw new IOException(ex); } } /** * Returns the ID string from the specified URI target. The ID string * should be between the first and second slash characters. */ private String parseId(String uriTarget) { int pos1 = uriTarget.indexOf("/"); String id = (pos1 < 0) ? uriTarget : uriTarget.substring(pos1 + 1); int pos2 = id.indexOf("/"); return (pos2 < 0) ? id : id.substring(0, pos2); } }