package org.xbmc.kore.ui.sections.localfile; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.net.wifi.WifiManager; import android.provider.OpenableColumns; import android.webkit.MimeTypeMap; import org.xbmc.kore.utils.LogUtils; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.math.BigInteger; import java.net.InetAddress; import java.net.UnknownHostException; import java.security.SecureRandom; import java.util.LinkedList; import java.util.List; import java.util.Map; import fi.iki.elonen.NanoHTTPD; import static android.content.Context.WIFI_SERVICE; public class HttpApp extends NanoHTTPD { private HttpApp(Context applicationContext, int port) throws IOException { super(port); this.applicationContext = applicationContext; this.localFileLocationList = new LinkedList<>(); this.localUriList = new LinkedList<>(); this.token = generateToken(); start(NanoHTTPD.SOCKET_READ_TIMEOUT, false); } private final int TOKEN_LENGTH = 12; private String generateToken() { String token = ""; SecureRandom sr = new SecureRandom(); for (int i = 0; i < TOKEN_LENGTH; i++) { int n = sr.nextInt(26*2 + 10); if (n < 26) { n += 'A'; } else if (n < 26*2) { n += 'a' - 26; } else { n += '0' - 26*2; } token += Character.toString((char) n); } return token; } private Context applicationContext; private LinkedList localFileLocationList = null; private LinkedList localUriList = null; private int currentIndex; private boolean currentIsFile; private String token; private final Response forbidden = newFixedLengthResponse(Response.Status.FORBIDDEN, "", ""); @Override public Response serve(IHTTPSession session) { Map> params = session.getParameters(); if (localFileLocationList == null) { return forbidden; } if (!params.containsKey("token")) { return forbidden; } if (!params.get("token").get(0).equals(this.token)) { return forbidden; } FileInputStream fis = null; String mimeType = null; try { if (params.containsKey("number")) { int file_number = Integer.parseInt(params.get("number").get(0)); LocalFileLocation localFileLocation = localFileLocationList.get(file_number); fis = new FileInputStream(localFileLocation.fullPath); mimeType = localFileLocation.getMimeType(); } else if (params.containsKey("uri")) { int uri_number = Integer.parseInt(params.get("uri").get(0)); fis = (FileInputStream) applicationContext.getContentResolver().openInputStream(localUriList.get(uri_number)); } else { return forbidden; } } catch (FileNotFoundException e) { LogUtils.LOGW(LogUtils.makeLogTag(HttpApp.class), e.toString()); return forbidden; } return newChunkedResponse(Response.Status.OK, mimeType, fis); } public void addLocalFilePath(LocalFileLocation localFileLocation) { if (localFileLocationList.contains(localFileLocation)) { // Path already exists, get its index: currentIndex = localFileLocationList.indexOf(localFileLocation); } else { this.localFileLocationList.add(localFileLocation); currentIndex = localFileLocationList.size() - 1; } currentIsFile = true; } public void addUri(Uri uri) { if (localUriList.contains(uri)) { currentIndex = localUriList.indexOf(uri); } else { this.localUriList.add(uri); currentIndex = localUriList.size() - 1; } currentIsFile = false; } private String getIpAddress() throws UnknownHostException { WifiManager wm = (WifiManager) applicationContext.getSystemService(WIFI_SERVICE); byte[] byte_address = BigInteger.valueOf(wm.getConnectionInfo().getIpAddress()).toByteArray(); // Reverse `byte_address`: for (int i = 0; i < byte_address.length/2; i++) { byte temp = byte_address[i]; int j = byte_address.length - i - 1; if (j < 0) break; byte_address[i] = byte_address[j]; byte_address[j] = temp; } InetAddress inet_address = InetAddress.getByAddress(byte_address); String ip = inet_address.getHostAddress(); return ip; } public String getLinkToFile() { String ip = null; try { ip = getIpAddress(); } catch (UnknownHostException uhe) { return null; } try { if (!isAlive()) start(); } catch (IOException ioe) { LogUtils.LOGE(LogUtils.makeLogTag(HttpApp.class), ioe.getMessage()); } String path = null; if (currentIsFile) { path = localFileLocationList.get(currentIndex).fileName + "?number=" + currentIndex; } else { Uri uri = localUriList.get(currentIndex); String filename = getFileNameFromUri(uri); path = filename + "?uri=" + currentIndex; } return "http://" + ip + ":" + getListeningPort() + "/" + path + "&token=" + token; } private String getFileNameFromUri(Uri contentUri) { String fileName = ""; // Let's parse the Uri to detect the filename: if (contentUri.toString().startsWith("content://")) { Cursor cursor = null; try { cursor = applicationContext.getContentResolver().query(contentUri, null, null, null, null); if (cursor != null && cursor.moveToFirst()) { fileName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)); } } finally { cursor.close(); } } // If the mimeType determined by Andoid is not equal to the one determined by // the filename, add an extra extension to make sure Kodi recognizes the file type: String mimeType = applicationContext.getContentResolver().getType(contentUri); MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton(); String extensionFromFilename = mimeTypeMap.getMimeTypeFromExtension( MimeTypeMap.getFileExtensionFromUrl(fileName)); if ( (extensionFromFilename == null) || (!extensionFromFilename.equals(mimeType)) ) { fileName += "." + MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType); } return fileName; } private static HttpApp http_app = null; public static HttpApp getInstance(Context applicationContext, int port) throws IOException { if (http_app == null) { synchronized (HttpApp.class) { if (http_app == null) { http_app = new HttpApp(applicationContext, port); } } } return http_app; } }