/* * Copyright 2015 The SageTV Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package sage; public clreplaced IOUtils { private IOUtils() { } public static String getFileExtension(java.io.File f) { String s = f == null ? "" : f.toString(); int idx = s.lastIndexOf('.'); if (idx < 0) return ""; else return s.substring(idx + 1); } // Returns the size of all files contained within the directory & recursively beneath public static long getDirectorySize(java.io.File f) { return getDirectorySize(f, new java.util.HashSet()); } private static long getDirectorySize(java.io.File f, java.util.HashSet done) { // protect against infinite recursion due to symbolic links on Linux java.io.File realF = f; try { // On Linux we need to resolve symbolic links or we could recurse forever realF = f.getCanonicalFile(); } catch (java.io.IOException e){} if (!done.add(realF)) return 0; long rv = 0; java.io.File[] kids = f.listFiles(); for (int i = 0; kids != null && i < kids.length; i++) { if (kids[i].isFile()) rv += kids[i].length(); else if (kids[i].isDirectory()) rv += getDirectorySize(kids[i]); } return rv; } // Returns the all files contained within the directory & recursively beneath. Does NOT return directories public static java.io.File[] listFilesRecursive(java.io.File f) { return listFilesRecursive(f, true); } public static java.io.File[] listFilesRecursive(java.io.File f, boolean includeAllFileTypes) { java.util.ArrayList rv = new java.util.ArrayList(); listFilesRecursive(f, rv, new java.util.HashSet(), includeAllFileTypes); return (java.io.File[]) rv.toArray(new java.io.File[0]); } private static void listFilesRecursive(java.io.File f, java.util.ArrayList rv, java.util.HashSet done, boolean includeAllFileTypes) { // protect against infinite recursion due to symbolic links on Linux java.io.File realF = f; try { // On Linux we need to resolve symbolic links or we could recurse forever realF = f.getCanonicalFile(); } catch (java.io.IOException e){} if (!done.add(realF)) return; java.io.File[] kids = f.listFiles(); for (int i = 0; kids != null && i < kids.length; i++) { if (kids[i].isFile()) { if (includeAllFileTypes || Seeker.getInstance().hasImportableFileExtension(kids[i].getName())) rv.add(kids[i]); } else if (kids[i].isDirectory()) listFilesRecursive(kids[i], rv, done, includeAllFileTypes); } } // Returns the all files contained within the directory & recursively beneath. Does NOT return directories public static java.io.File[] listServerFilesRecursive(java.io.File f) { return listServerFilesRecursive(f, true); } public static java.io.File[] listServerFilesRecursive(java.io.File f, boolean includeAllFileTypes) { if (!Sage.client) return new java.io.File[0]; java.net.Socket sock = null; java.io.DataOutputStream outStream = null; java.io.DataInputStream inStream = null; try { sock = new java.net.Socket(); sock.connect(new java.net.InetSocketAddress(Sage.preferredServer, 7818), 5000); sock.setSoTimeout(30000); //sock.setTcpNoDelay(true); outStream = new java.io.DataOutputStream(new java.io.BufferedOutputStream(sock.getOutputStream())); inStream = new java.io.DataInputStream(new java.io.BufferedInputStream(sock.getInputStream())); // First request access to it since it's probably not in the media filesystem boolean gotMSAccess = false; if (NetworkClient.getSN().requestMediaServerAccess(f, true)) { gotMSAccess = true; } outStream.write((includeAllFileTypes ? "LISTRECURSIVEALLW " : "LISTRECURSIVEW").getBytes(sage.Sage.BYTE_CHARSET)); outStream.write(f.toString().getBytes("UTF-16BE")); outStream.write("\r\n".getBytes(sage.Sage.BYTE_CHARSET)); outStream.flush(); String str = sage.Sage.readLineBytes(inStream); if (!"OK".equals(str)) throw new java.io.IOException("Error doing remote recursive dir listing of:" + str); // get the size str = sage.Sage.readLineBytes(inStream); int numFiles = Integer.parseInt(str); java.io.File[] rv = new java.io.File[numFiles]; for (int i = 0; i < numFiles; i++) { rv[i] = new java.io.File(sage.MediaServer.convertToUnicode(sage.Sage.readLineBytes(inStream))); } if (gotMSAccess) { NetworkClient.getSN().requestMediaServerAccess(f, false); } return rv; } catch (java.io.IOException e) { if (Sage.DBG) System.out.println("ERROR downloading recursive directory listing from server of :" + e + " for dir: " + f); return new java.io.File[0]; } finally { try{ if (sock != null) sock.close(); }catch (Exception e1){} try{ if (outStream != null) outStream.close(); }catch (Exception e2){} try{ if (inStream != null) inStream.close(); }catch (Exception e3){} } } // Returns the list of all files contained within the specified directory public static String[] listServerFiles(java.io.File f) { if (!Sage.client) return Pooler.EMPTY_STRING_ARRAY; java.net.Socket sock = null; java.io.DataOutputStream outStream = null; java.io.DataInputStream inStream = null; try { sock = new java.net.Socket(); sock.connect(new java.net.InetSocketAddress(Sage.preferredServer, 7818), 5000); sock.setSoTimeout(30000); //sock.setTcpNoDelay(true); outStream = new java.io.DataOutputStream(new java.io.BufferedOutputStream(sock.getOutputStream())); inStream = new java.io.DataInputStream(new java.io.BufferedInputStream(sock.getInputStream())); // First request access to it since it's probably not in the media filesystem boolean gotMSAccess = false; if (NetworkClient.getSN().requestMediaServerAccess(f, true)) { gotMSAccess = true; } outStream.write("LISTW ".getBytes(sage.Sage.BYTE_CHARSET)); outStream.write(f.toString().getBytes("UTF-16BE")); outStream.write("\r\n".getBytes(sage.Sage.BYTE_CHARSET)); outStream.flush(); String str = sage.Sage.readLineBytes(inStream); if (!"OK".equals(str)) throw new java.io.IOException("Error doing remote recursive dir listing of:" + str); // get the size str = sage.Sage.readLineBytes(inStream); int numFiles = Integer.parseInt(str); String[] rv = new String[numFiles]; for (int i = 0; i < numFiles; i++) { rv[i] = sage.MediaServer.convertToUnicode(sage.Sage.readLineBytes(inStream)); } if (gotMSAccess) { NetworkClient.getSN().requestMediaServerAccess(f, false); } return rv; } catch (java.io.IOException e) { if (Sage.DBG) System.out.println("ERROR downloading recursive directory listing from server of :" + e + " for dir: " + f); return Pooler.EMPTY_STRING_ARRAY; } finally { try{ if (sock != null) sock.close(); }catch (Exception e1){} try{ if (outStream != null) outStream.close(); }catch (Exception e2){} try{ if (inStream != null) inStream.close(); }catch (Exception e3){} } } public static java.io.File getRootDirectory(java.io.File f) { if (f == null) return null; int numParents = 0; java.io.File currParent = f; while (currParent.getParentFile() != null) { currParent = currParent.getParentFile(); numParents++; } if (Sage.EMBEDDED) { String pathStr = f.getAbsolutePath(); boolean useOurCheck = false; if (pathStr.startsWith("/var/media/") || pathStr.startsWith("/tmp/external/")) { numParents -= 3; useOurCheck = true; } else if (pathStr.startsWith("/tmp/sagetv_shares/")) { numParents -= 4; useOurCheck = true; } if (useOurCheck) { while (numParents-- > 0 && f != null) f = f.getParentFile(); return f; } } if (currParent.toString().equals("\\\\") || (Sage.MAC_OS_X && f.toString().startsWith("/Volumes"))) { // UNC Pathname, add the computer name and share name to get the actual root folder // or on Mac we need to protect the /Volumes directory numParents -= 2; while (numParents-- > 0) f = f.getParentFile(); return f; } else return currParent; } public static void copyFile(java.io.File srcFile, java.io.File destFile) throws java.io.IOException { java.io.FileOutputStream fos = null; java.io.OutputStream outStream = null; java.io.InputStream inStream = null; try { outStream = new java.io.BufferedOutputStream(fos = new java.io.FileOutputStream(destFile)); inStream = new java.io.BufferedInputStream(new java.io.FileInputStream(srcFile)); byte[] buf = new byte[65536]; int numRead = inStream.read(buf); while (numRead != -1) { outStream.write(buf, 0, numRead); numRead = inStream.read(buf); } } finally { try { if (inStream != null) { inStream.close(); inStream = null; } } catch (java.io.IOException e) {} try { if (outStream != null) { outStream.flush(); fos.getFD().sync(); outStream.close(); outStream = null; } } catch (java.io.IOException e) {} } // Preserve the file timestamp on the copy as well destFile.setLastModified(srcFile.lastModified()); } public static String exec(String[] cmdArray) { return exec(cmdArray, true, true); } public static String exec(String[] cmdArray, final boolean getStderr, final boolean getStdout) { return exec(cmdArray, getStderr, getStdout, false); } public static String exec(String[] cmdArray, final boolean getStderr, final boolean getStdout, final boolean controlRunaways) { return exec(cmdArray, getStderr, getStdout, controlRunaways, null); } public static String exec(String[] cmdArray, final boolean getStderr, final boolean getStdout, final boolean controlRunaways, java.io.File workingDir) { return (String) exec(cmdArray, getStderr, getStdout, controlRunaways, workingDir, false); } public static java.io.ByteArrayOutputStream execByteOutput(String[] cmdArray, final boolean getStderr, final boolean getStdout, final boolean controlRunaways, java.io.File workingDir) { return (java.io.ByteArrayOutputStream) exec(cmdArray, getStderr, getStdout, controlRunaways, workingDir, true); } private static Object exec(String[] cmdArray, final boolean getStderr, final boolean getStdout, final boolean controlRunaways, java.io.File workingDir, boolean byteOutput) { final long timeLimit = controlRunaways ? Sage.getLong("control_runaway_exec_time_limit", 60000) : 0; final long sizeLimit = controlRunaways ? Sage.getLong("control_runaway_exec_size_limit", 1024*1024) : 0; try { final Process procy = Runtime.getRuntime().exec(cmdArray, null, workingDir); final java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(1024); final long startTime = Sage.time(); Thread the = new Thread("InputStreamConsumer") { public void run() { try { java.io.InputStream buf = procy.getInputStream(); String s; do { int c = buf.read(); if (c == -1) break; else if (getStdout) baos.write(c); if (sizeLimit > 0 && baos.size() > sizeLimit) { if (Sage.DBG) System.out.println("NOTICE: Forcibly terminating spawned process due to runaway memory detection!"); procy.destroy(); break; } }while (true); buf.close(); } catch (Exception e){} } }; the.setDaemon(true); the.start(); Thread the2 = new Thread("ErrorStreamConsumer") { public void run() { try { java.io.InputStream buf = procy.getErrorStream(); String s; do { int c = buf.read(); if (c == -1) break; else if (getStderr) baos.write(c); if (sizeLimit > 0 && baos.size() > sizeLimit) { if (Sage.DBG) System.out.println("NOTICE: Forcibly terminating spawned process due to runaway memory detection!"); procy.destroy(); break; } }while (true); buf.close(); } catch (Exception e){} } }; the2.setDaemon(true); the2.start(); if (controlRunaways) { final boolean[] doneWaitin = new boolean[1]; doneWaitin[0] = false; Pooler.execute(new Runnable() { public void run() { try { procy.waitFor(); } catch (Exception e) { } finally { synchronized (doneWaitin) { doneWaitin[0] = true; doneWaitin.notifyAll(); } } } }); synchronized (doneWaitin) { while (!doneWaitin[0] && Sage.time() - startTime < timeLimit) { doneWaitin.wait(Math.max(1, timeLimit - (Sage.time() - startTime))); } if (!doneWaitin[0]) { if (Sage.DBG) System.out.println("NOTICE: Forcibly terminating spawned process due to runaway execution detection!"); procy.destroy(); } } } else procy.waitFor(); the.join(1000); the2.join(1000); if (byteOutput) { return baos; } if (Sage.EMBEDDED) { // If we don't use the default encoding here then some chars don't get converted right and the string is truncated. // The test case is the Aeon Flux .mov file in DemoContent return baos.toString(); } try { return baos.toString(Sage.I18N_CHARSET); } catch (java.io.UnsupportedEncodingException uee) { // just use the default encoding return baos.toString(); } } catch (Exception e) { if (byteOutput) return null; return e.toString(); } } public static int exec2(String[] cmdArray) { return exec3(cmdArray, true); } public static int exec2(String[] cmdArray, boolean waitForExit) { return exec3(cmdArray, waitForExit); } public static int exec2(String cmd) { return exec3(cmd, true); } public static int exec2(String cmd, boolean waitForExit) { return exec3(cmd, waitForExit); } private static int exec3(Object obj, boolean waitForExit) { if (Sage.DBG) System.out.println("Executing process: " + ((obj instanceof String[]) ? java.util.Arrays.asList((String[])obj) : obj)); try { final Process procy = obj instanceof String[] ? Runtime.getRuntime().exec((String[])obj) : Runtime.getRuntime().exec((String) obj); if (Sage.DBG) System.out.println("Started process object: " + procy); Thread the = new Thread("InputStreamConsumer") { public void run() { try { java.io.BufferedReader buf = new java.io.BufferedReader(new java.io.InputStreamReader(procy.getInputStream())); String s; do { s = buf.readLine(); if (s == null) break; else { if (Sage.DBG) System.out.println("STDOUT:" + procy + ": " + s); } }while (true); buf.close(); } catch (Exception e){} } }; the.setDaemon(true); the.start(); Thread the2 = new Thread("ErrorStreamConsumer") { public void run() { try { java.io.BufferedReader buf = new java.io.BufferedReader(new java.io.InputStreamReader(procy.getErrorStream())); String s; do { s = buf.readLine(); if (s == null) break; else { if (Sage.DBG) System.out.println("STDERR:" + procy + ": " + s); } }while (true); buf.close(); } catch (Exception e){} } }; the2.setDaemon(true); the2.start(); if (waitForExit) { procy.waitFor(); the.join(1000); the2.join(1000); return procy.exitValue(); } else return 0; } catch (Exception e) { if (Sage.DBG) System.out.println("Error executing process " + ((obj instanceof String[]) ? (((String[])obj)[0]) : obj) + " : " + e); return -1; } } public static boolean deleteDirectory(java.io.File dir) { if (dir == null) return false; java.io.File[] kids = dir.listFiles(); boolean rv = true; for (int i = 0; i < kids.length; i++) { if (kids[i].isDirectory()) { rv &= deleteDirectory(kids[i]); } else if (!kids[i].delete()) rv = false; } return dir.delete() && rv; } public static String getFilereplacedtring(java.io.File file) { java.io.BufferedReader buffRead = null; StringBuffer sb = new StringBuffer(); try { buffRead = new java.io.BufferedReader(new java.io.FileReader(file)); char[] cbuf = new char[4096]; int numRead = buffRead.read(cbuf); while (numRead != -1) { sb.append(cbuf, 0, numRead); numRead = buffRead.read(cbuf); } } catch (java.io.IOException e) { System.out.println("Error reading file " + file + " of: " + e); } finally { if (buffRead != null) { try{buffRead.close();}catch(Exception e){} buffRead = null; } } return sb.toString(); } public static String convertPlatformPathChars(String str) { StringBuffer sb = null; int strlen = str.length(); char replaceChar = java.io.File.separatorChar; char testChar = (replaceChar == '/') ? '\\' : '/'; for (int i = 0; i < strlen; i++) { char c = str.charAt(i); if (c == testChar) { if (sb == null) { sb = new StringBuffer(str.length()); sb.append(str.substring(0, i)); } sb.append(replaceChar); } else if (sb != null) sb.append(c); } if (sb == null) return str; else return sb.toString(); } public static java.net.InetAddress getSubnetMask() { return getSubnetMask(null); } public static java.net.InetAddress getSubnetMask(java.net.InetAddress adapterAddr) { if (Sage.EMBEDDED) { try { String eth0Info = IOUtils.exec(new String[] { "ifconfig", "eth0" }); int idx0 = eth0Info.indexOf("Mask:"); if (idx0 != -1) { int idx1 = eth0Info.indexOf("\n", idx0); if (idx1 != -1) { return java.net.InetAddress.getByName(eth0Info.substring(idx0 + "Mask:".length(), idx1).trim()); } } return java.net.InetAddress.getByName("255.255.255.0"); }catch(Throwable e) { System.out.println("ERROR:" + e); } return null; } else { String ipAddrInfo = exec(new String[] { Sage.WINDOWS_OS ? "ipconfig" : "ifconfig"}); java.util.regex.Pattern patty = java.util.regex.Pattern.compile("255\\.255\\.[0-9]+\\.[0-9]+"); java.util.regex.Matcher matchy = patty.matcher(ipAddrInfo); try { int adapterIndex = (adapterAddr == null) ? -1 : ipAddrInfo.indexOf(adapterAddr.getHostAddress()); while (matchy.find()) { String currMatch = matchy.group(); if ("255.255.255.255".equals(currMatch)) continue; // ignore the subnet masks that restrict all since they're not what we want // Make sure we're on the network adapter of interest if (matchy.start() > adapterIndex) return java.net.InetAddress.getByName(currMatch); } return java.net.InetAddress.getByName("255.255.255.0"); } catch (java.net.UnknownHostException e) { throw new RuntimeException(e); } } } // This returns the 40-byte MAC address public static byte[] getMACAddress() { final String[] macBuf = new String[1]; try { macBuf[0] = sage.Sage.getMACAddress0(); if(macBuf[0] != null) { byte[] rv = new byte[6]; // The first digit is NOT always zero, don't skip it! for (int i = 0; i < macBuf[0].length(); i+=3) { rv[(i/3)] = (byte)(Integer.parseInt(macBuf[0].substring(i, i+2), 16) & 0xFF); } return rv; } } catch(Throwable t) {} try { String forcedMAC = Sage.get("forced_mac_address", null); if (forcedMAC != null && forcedMAC.length() > 0) { if (Sage.DBG) System.out.println("Using forced MAC address of: " + forcedMAC); macBuf[0] = forcedMAC; } else { String prefix; final Process procy = Runtime.getRuntime().exec(Sage.WINDOWS_OS ? "ipconfig /all" : "ifconfig", null, null); final java.util.regex.Pattern pat;// = java.util.regex.Pattern.compile(MiniClient.WINDOWS_OS ? //"Physical Address(\\. )*\\: (\\p{XDigit}\\p{XDigit}\\-\\p{XDigit}\\p{XDigit}\\-\\p{XDigit}\\p{XDigit}\\-\\p{XDigit}\\p{XDigit}\\-\\p{XDigit}\\p{XDigit}\\-\\p{XDigit}\\p{XDigit})" : //"(\\p{XDigit}\\p{XDigit}\\:\\p{XDigit}\\p{XDigit}\\:\\p{XDigit}\\p{XDigit}\\:\\p{XDigit}\\p{XDigit}\\:\\p{XDigit}\\p{XDigit}\\:\\p{XDigit}\\p{XDigit})"); if (Sage.WINDOWS_OS) prefix = ""; else if (Sage.MAC_OS_X) prefix = "ether"; else prefix = ""; // no prefix for linux since language changes the label pat = java.util.regex.Pattern.compile(prefix + " ((\\p{XDigit}{2}[:-]){5}\\p{XDigit}{2})"); Thread the = new Thread("InputStreamConsumer") { public void run() { try { java.io.BufferedReader buf = new java.io.BufferedReader(new java.io.InputStreamReader( procy.getInputStream())); String s; macBuf[0] = null; while ((s = buf.readLine()) != null) { java.util.regex.Matcher m = pat.matcher(s); // in case there's multiple adapters we only want the first one if (macBuf[0] == null && m.find()) { macBuf[0] = m.group(1); } } buf.close(); } catch (Exception e){} } }; the.setDaemon(true); the.start(); Thread the2 = new Thread("ErrorStreamConsumer") { public void run() { try { java.io.BufferedReader buf = new java.io.BufferedReader(new java.io.InputStreamReader( procy.getErrorStream())); while (buf.readLine() != null); buf.close(); } catch (Exception e){} } }; the2.setDaemon(true); the2.start(); the.join(); the2.join(); procy.waitFor(); } if (macBuf[0] != null) { byte[] rv = new byte[6]; // The first digit is NOT always zero, so don't skip it for (int i = 0; i < macBuf[0].length(); i+=3) { rv[(i/3)] = (byte)(Integer.parseInt(macBuf[0].substring(i, i+2), 16) & 0xFF); } return rv; } else return null; } catch (Exception e) { System.out.println("Error getting MAC address of:" + e); return null; } } // Returns true if this socket connection came from the localhost public static boolean isLocalhostSocket(java.net.Socket sake) { byte[] localIP = sake.getLocalAddress().getAddress(); byte[] remoteIP = sake.getInetAddress().getAddress(); return ((remoteIP[0] == 127 && remoteIP[1] == 0 && remoteIP[2] == 0 && remoteIP[3] == 1) || (remoteIP[0] == localIP[0] && remoteIP[1] == localIP[1] && remoteIP[2] == localIP[2] && remoteIP[3] == localIP[3])); } public static boolean safeEquals(Object o1, Object o2) { return (o1 == o2) || (o1 != null && o2 != null && o1.equals(o2)); } // This returns a UTF-8 string on Windows, otherwise it just returns the string. // This is used for preplaceding filenames to non-Unicode apps like FFMPEG public static String getLibAVFilenameString(String s) { if (Sage.WINDOWS_OS) { // If any non-ASCII characters are in this string; then create a temp file and put it in there instead. // This works around an issue where the Windows OS will do a codepage conversion on the UTF-8 values we're preplaceding // on the command line. And also the Java bug where it doesn't send Unicode parameters to other processes. // We still write the bytes in the temp file as UTF-8 format though. boolean hasUni = false; for (int i = 0; i < s.length(); i++) { int c = s.charAt(i) & 0xFFFF; if (c > 127) { hasUni = true; break; } } if (hasUni) { try { java.io.File tmpFile = java.io.File.createTempFile("stvfm", ".txt"); tmpFile.deleteOnExit(); byte[] strBytes = s.getBytes("UTF-8"); java.io.OutputStream os = new java.io.FileOutputStream(tmpFile); os.write(strBytes); os.close(); return tmpFile.getAbsolutePath(); } catch (java.io.IOException ex) { if (Sage.DBG) System.out.println("Error creating temp file for UTF-8 parameter preplaceding:" + ex); return s; } } else return s; } else return s; } public static boolean safemkdirs(java.io.File f) { if (Sage.MAC_OS_X) { String fstr = f.toString(); if (!fstr.startsWith("/Volumes/")) return f.mkdirs(); // We can create all the dirs up until the one below Volumes java.util.Stack dirStack = new java.util.Stack(); java.io.File currParent = null; while (true) { currParent = f.getParentFile(); if (currParent == null) return false; if (currParent.getName().equals("Volumes")) { // We found the volumes root parent so we break out and create all the dirs on the stack break; } dirStack.push(f); f = currParent; } while (!dirStack.isEmpty()) { if (!((java.io.File) dirStack.pop()).mkdir()) return false; } return true; } else return f.mkdirs(); } // Requires positive x static int stringSize(long x) { long p = 10; for (int i=1; i<19; i++) { if (x < p) return i; p = 10*p; } return 19; } final static byte[] digits = { '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' , 'n' , 'o' , 'p' , 'q' , 'r' , 's' , 't' , 'u' , 'v' , 'w' , 'x' , 'y' , 'z' }; final static byte [] DigitTens = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', '3', '3', '3', '3', '3', '3', '3', '3', '3', '3', '4', '4', '4', '4', '4', '4', '4', '4', '4', '4', '5', '5', '5', '5', '5', '5', '5', '5', '5', '5', '6', '6', '6', '6', '6', '6', '6', '6', '6', '6', '7', '7', '7', '7', '7', '7', '7', '7', '7', '7', '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', '9', '9', '9', '9', '9', '9', '9', '9', '9', '9', } ; final static byte [] DigitOnes = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', } ; private static final byte[] LONG_MIN_VALUE_STRING_BYTES = "-9223372036854775808".getBytes(); // Returns the number of bytes written into the array public static int printLongInByteArray(long i, byte[] dest, int offset) { if (i == Long.MIN_VALUE) { System.arraycopy(LONG_MIN_VALUE_STRING_BYTES, 0, dest, offset, LONG_MIN_VALUE_STRING_BYTES.length); return LONG_MIN_VALUE_STRING_BYTES.length; } int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); long q; int r; int charPos = size + offset; char sign = 0; if (i < 0) { sign = '-'; i = -i; } // Get 2 digits/iteration using longs until quotient fits into an int while (i > Integer.MAX_VALUE) { q = i / 100; // really: r = i - (q * 100); r = (int)(i - ((q << 6) + (q << 5) + (q << 2))); i = q; dest[--charPos] = DigitOnes[r]; dest[--charPos] = DigitTens[r]; } // Get 2 digits/iteration using ints int q2; int i2 = (int)i; while (i2 >= 65536) { q2 = i2 / 100; // really: r = i2 - (q * 100); r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2)); i2 = q2; dest[--charPos] = DigitOnes[r]; dest[--charPos] = DigitTens[r]; } // Fall thru to fast mode for smaller numbers // replacedert(i2 <= 65536, i2); for (;;) { q2 = (i2 * 52429) >>> (16+3); r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ... dest[--charPos] = digits[r]; i2 = q2; if (i2 == 0) break; } if (sign != 0) { dest[--charPos] = (byte)sign; } return size; } public static boolean isLocalhostAddress(java.net.InetAddress inetAddress) { byte[] remoteIP = inetAddress.getAddress(); if (remoteIP[0] == 127 && remoteIP[1] == 0 && remoteIP[2] == 0 && remoteIP[3] == 1) return true; try { byte[] localIP = java.net.InetAddress.getLocalHost().getAddress(); return (remoteIP[0] == localIP[0] && remoteIP[1] == localIP[1] && remoteIP[2] == localIP[2] && remoteIP[3] == localIP[3]); } catch (Exception e) { System.out.println("ERROR getting localhost address of:" + e); } return false; } static int _IOC_NRBITS = 8; static int _IOC_TYPEBITS = 8; static int _IOC_SIZEBITS = 14; static int _IOC_DIRBITS = 2; static int _IOC_NRMASK = ((1 << _IOC_NRBITS)-1); static int _IOC_TYPEMASK = ((1 << _IOC_TYPEBITS)-1); static int _IOC_SIZEMASK = ((1 << _IOC_SIZEBITS)-1); static int _IOC_DIRMASK = ((1 << _IOC_DIRBITS)-1); static int _IOC_NRSHIFT = 0; static int _IOC_TYPESHIFT = (_IOC_NRSHIFT+_IOC_NRBITS); static int _IOC_SIZESHIFT = (_IOC_TYPESHIFT+_IOC_TYPEBITS); static int _IOC_DIRSHIFT = (_IOC_SIZESHIFT+_IOC_SIZEBITS); static int _IOC_NONE = 0; static int _IOC_WRITE = 1; static int _IOC_READ = 2; static int _IOW(int type, int nr, int size) { int dir=_IOC_WRITE; return (((dir) << _IOC_DIRSHIFT) | ((type) << _IOC_TYPESHIFT) | ((nr) << _IOC_NRSHIFT) | ((size) << _IOC_SIZESHIFT)); } static int IOCTL_USB_CONNECTINFO = _IOW('U', 17, 8 /* struct usb_connectinfo */); static long getUSBHWID() { // First get what is supposed to be the serial number; and then verify it exists String targetSerial = exec(new String[] { "tbutil", "getserial" }); // Now convert this to the 64-bit long for the targetSerialID long targetSerialID = 0; int targetBitOffset = 0; try { int idx = 0; long currRead = targetSerial.charAt(idx++); while (true) { currRead = Integer.parseInt(((char) currRead) + "", 16); targetSerialID = targetSerialID ^ ((currRead & 0xF) << targetBitOffset); if (idx >= targetSerial.length()) break; currRead = targetSerial.charAt(idx++); targetBitOffset += 4; targetBitOffset = targetBitOffset % 64; } } catch (Exception nfe) { } // Check in /sys/bus/usb/devices to find anything in there that has a 'serial' field; but only check the ones // that start with a number. String[] usbdevids = new java.io.File("/sys/bus/usb/devices").list(); for (int i = 0; usbdevids != null && i < usbdevids.length; i++) { //System.out.println("Checking USB Dev ID=" + usbdevids[i]); if (Character.isDigit(usbdevids[i].charAt(0))) { // Check for the 'serial' field java.io.File serialFile = new java.io.File("/sys/bus/usb/devices/" + usbdevids[i] + "/serial"); if (serialFile.isFile()) { //System.out.println("Serial exists for this device..."); int usbMajor = Integer.parseInt(usbdevids[i].substring(0, 1)); java.io.Reader inReader = null; int usbMinor = -1; try { inReader = new java.io.FileReader("/sys/bus/usb/devices/" + usbdevids[i] + "/devnum"); usbMinor = Integer.parseInt(((char)inReader.read()) + ""); inReader.close(); inReader = null; } catch (Exception e) { continue; } java.text.DecimalFormat leadZeros = new java.text.DecimalFormat("000"); //System.out.println("USB dev num " + usbMajor + "-" + usbMinor); String verificationDevice = "/dev/bus/usb/" + leadZeros.format(usbMajor) + "/" + leadZeros.format(usbMinor); //System.out.println("Verifying w/ device:" + verificationDevice); int usb_fd = -1; byte[] buf1 = new byte[8]; byte[] desc = new byte[18]; try { usb_fd = jtux.UFile.open(verificationDevice, jtux.UConstant.O_RDWR); //System.out.println("ioctl "+IOCTL_USB_CONNECTINFO); int retval = jtux.UFile.ioctl(usb_fd, IOCTL_USB_CONNECTINFO, buf1); if(retval<0) { //System.out.println("Error "+retval); continue; } else { //these bufs create the 'devnum', but you'll need to check endian in java.nio.Buffer int checkedDevNum = 0; if(java.nio.ByteOrder.nativeOrder() != java.nio.ByteOrder.BIG_ENDIAN) checkedDevNum = ((buf1[3] & 0xFF) << 24) | ((buf1[2] & 0xFF) << 16) | ((buf1[1] & 0xFF) << 8) | (buf1[0] & 0xFF); else checkedDevNum = ((buf1[0] & 0xFF) << 24) | ((buf1[1] & 0xFF) << 16) | ((buf1[2] & 0xFF) << 8) | (buf1[3] & 0xFF); //System.out.println("checked dev num=" + checkedDevNum); if (checkedDevNum != usbMinor) continue; } jtux.UFile.read(usb_fd, desc, 18); // also make sure the serial index is non-zero //System.out.println("Manuf index "+ (desc[14]&0xFF) + // " Product index "+ (desc[15]&0xFF) + // " Serial index "+ (desc[16]&0xFF)); if ((desc[16] & 0xFF) == 0) continue; } catch (jtux.UErrorException e) { //System.out.println(e); continue; } finally { try { jtux.UFile.close(usb_fd); } catch (jtux.UErrorException e1) { } usb_fd=-1; } // Now read the serial and convert it to a 64-bit integer to return long rv = 0; int rvBitOffset = 0; try { inReader = new java.io.FileReader("/sys/bus/usb/devices/" + usbdevids[i] + "/serial"); long currRead = inReader.read(); while (currRead != -1) { currRead = Integer.parseInt(((char) currRead) + "", 16); rv = rv ^ ((currRead & 0xF) << rvBitOffset); //System.out.println("Updating HWID rv=" + rv + " currRead=" + currRead + " rvBitOffset=" + rvBitOffset); currRead = inReader.read(); rvBitOffset += 4; rvBitOffset = rvBitOffset % 64; } inReader.close(); inReader = null; } catch (NumberFormatException nfe) { // This can happen reading the line terminator if (inReader != null) { try { inReader.close(); inReader = null; } catch (Exception e2){} } } catch (Exception e) { continue; } if (targetSerialID != 0 && Math.abs(targetSerialID) != Math.abs(rv)) continue; return Math.abs(rv); } } } return 0; } public static final int SMB_MOUNT_EXISTS = 0; public static final int SMB_MOUNT_SUCCEEDED = 1; public static final int SMB_MOUNT_FAILED = -1; public static Boolean has_smbmount; public static int doSMBMount(String smbPath, String localPath) { if (smbPath.startsWith("smb://")) smbPath = smbPath.substring(4); // Check if the mount is already done String grepStr; if (Sage.EMBEDDED) grepStr = "mount -t cifs | grep -i \"" + localPath + "\""; else grepStr = "mount -t smbfs | grep -i \"" + localPath + "\""; if (IOUtils.exec2(new String[] { "sh", "-c", grepStr }) == 0) { //if (Sage.DBG) System.out.println("SMB Mount already exists"); return SMB_MOUNT_EXISTS; } else { if (Sage.DBG) System.out.println("SMB Mount Path: " + smbPath + " " + localPath); new java.io.File(localPath).mkdirs(); // Extract any authentication information String smbUser = null; String smbPreplaced = null; int authIdx = smbPath.indexOf('@'); if (authIdx != -1) { int colonIdx = smbPath.indexOf(':'); if (colonIdx != -1) { smbUser = smbPath.substring(2, colonIdx); smbPreplaced = smbPath.substring(colonIdx + 1, authIdx); smbPath = "//" + smbPath.substring(authIdx + 1); } } if (!Sage.EMBEDDED) { String smbOptions; if (Sage.LINUX_OS) { if (smbUser != null) smbOptions = "username=" + smbUser + ",preplacedword=" + smbPreplaced + ",iocharset=utf8"; else smbOptions = "guest,iocharset=utf8"; } else { if (smbUser != null) smbOptions = smbUser + ":" + smbPreplaced; else smbOptions = "guest:"; } // check to see if property exists if it doesn't check for smbmount with "which" command if (IOUtils.has_smbmount == null) { String result = IOUtils.exec(new String[] {"which", "smbmount"}); // if nothing returned from "which" command then smbmount is not present so set property false if (result == null || result.length() == 0) { IOUtils.has_smbmount = Boolean.FALSE; } else { IOUtils.has_smbmount = Boolean.TRUE; } } // set execution variable based on static Boolean value String execSMBMount = IOUtils.has_smbmount ? "smbmount" : "mount.cifs"; if (IOUtils.exec2(Sage.LINUX_OS ? new String[] { execSMBMount, smbPath, localPath , "-o", smbOptions } : new String[] { "mount_smbfs", "-N", "//" + smbOptions + "@" + smbPath.substring(2), localPath}) == 0) { if (Sage.DBG) System.out.println("SMB Mount Succeeded"); return SMB_MOUNT_SUCCEEDED; } else { if (Sage.DBG) System.out.println("SMB Mount Failed"); return SMB_MOUNT_FAILED; } } else { // Resolve the SMB name to an IP address since we don't have proper NetBIOS resolution on this // device. String netbiosName = smbPath.substring(2, smbPath.indexOf('/', 3)); String smbShareName = smbPath.substring(smbPath.indexOf('/', 3) + 1); if (smbShareName.endsWith("/")) smbShareName = smbShareName.substring(0, smbShareName.length() - 1); String tmpSmbPath = smbPath; try { tmpSmbPath = "//" + jcifs.netbios.NbtAddress.getByName(netbiosName).getHostAddress() + smbPath.substring(smbPath.indexOf('/', 3)); if (Sage.DBG) System.out.println("Updated SMB path with IP address to be: " + tmpSmbPath); } catch (java.net.UnknownHostException uhe) { if (Sage.DBG) System.out.println("Error with NETBIOS naming lookup of:" + uhe); uhe.printStackTrace(); // If we can't resolve the netbios name then there's no way the mount will succeed so don't bother // since sometimes mount.cifs can hang if it can't resolve something appropriately return SMB_MOUNT_FAILED; } String smbOptions; if (Sage.LINUX_OS) { if (smbUser != null) smbOptions = "username=" + smbUser + ",preplacedword=" + smbPreplaced + ",iocharset=utf8,nounix"; else smbOptions = "guest,iocharset=utf8,nounix"; } else { if (smbUser != null) smbOptions = smbUser + ":" + smbPreplaced; else smbOptions = "guest:"; } int smbRes; if ((smbRes = IOUtils.exec2(Sage.LINUX_OS ? new String[] { "mount.cifs", tmpSmbPath, localPath , "-o", smbOptions } : new String[] { "mount_smbfs", "-N", "//" + smbOptions + "@" + smbPath.substring(2), localPath})) == 0) { if (Sage.DBG) System.out.println("SMB Mount Succeeded"); return SMB_MOUNT_SUCCEEDED; } else { if (Sage.DBG) System.out.println("SMB Mount Failed res=" + smbRes); if (Sage.LINUX_OS && smbUser == null) { if (Sage.DBG) System.out.println("Retrying SMB mount with share access..."); smbOptions = "username=share,guest,iocharset=utf8"; if ((smbRes = IOUtils.exec2(new String[] { "mount.cifs", tmpSmbPath, localPath , "-o", smbOptions })) == 0) { if (Sage.DBG) System.out.println("SMB Mount Succeeded"); return SMB_MOUNT_SUCCEEDED; } else { if (Sage.DBG) System.out.println("Retrying SMB mount with share access(2)..."); smbOptions = "username=" + smbShareName + ",guest,iocharset=utf8"; if ((smbRes = IOUtils.exec2(new String[] { "mount.cifs", tmpSmbPath, localPath , "-o", smbOptions })) == 0) { if (Sage.DBG) System.out.println("SMB Mount Succeeded"); return SMB_MOUNT_SUCCEEDED; } else { if (Sage.DBG) System.out.println("SMB Mount Failed res=" + smbRes); return SMB_MOUNT_FAILED; } } } return SMB_MOUNT_FAILED; } } } } public static final int NFS_MOUNT_EXISTS = 0; public static final int NFS_MOUNT_SUCCEEDED = 1; public static final int NFS_MOUNT_FAILED = -1; public static int doNFSMount(String nfsPath, String localPath) { if (!Sage.LINUX_OS) return NFS_MOUNT_FAILED; if (nfsPath.startsWith("nfs://")) nfsPath = nfsPath.substring(6); // Check if the mount is already done if (IOUtils.exec2(new String[] { "sh", "-c", "mount -t nfs | grep -i \"" + localPath + "\"" }) == 0) { //if (Sage.DBG) System.out.println("NFS Mount already exists"); return NFS_MOUNT_EXISTS; } else { if (Sage.DBG) System.out.println("NFS Mount Path: " + nfsPath + " " + localPath); new java.io.File(localPath).mkdirs(); String nfsOptions = "nolock,tcp,rsize=32768,wsize=32768,noatime"; int nfsRes = IOUtils.exec2(new String[] {"mount", "-t", "nfs", nfsPath, localPath, "-o", nfsOptions}); if (nfsRes == 0) { if (Sage.DBG) System.out.println("NFS Mount Succeeded"); return NFS_MOUNT_SUCCEEDED; } else { if (Sage.DBG) System.out.println("NFS Mount Failed res=" + nfsRes); return NFS_MOUNT_FAILED; } } } public static boolean undoMount(String currPath) { return IOUtils.exec2(new String[] { "umount", currPath}) == 0; } public static String convertSMBURLToUNCPath(String smbPath) { StringBuffer sb = new StringBuffer("\\\\" + smbPath.substring("smb://".length())); for (int i = 0; i < sb.length(); i++) if (sb.charAt(i) == '/') sb.setCharAt(i, '\\'); return sb.toString(); } // Creates a java.io.BufferedReader for the specified file and checks the first two bytes for the Unicode BOM and sets the // charset accordingly; otherwise if there's no BOM it'll use the preplaceded in defaultCharset public static java.io.BufferedReader openReaderDetectCharset(String filePath, String defaultCharset) throws java.io.IOException { return openReaderDetectCharset(new java.io.File(filePath), defaultCharset); } public static java.io.BufferedReader openReaderDetectCharset(java.io.File filePath, String defaultCharset) throws java.io.IOException { java.io.FileInputStream fis = null; try { fis = new java.io.FileInputStream(filePath); int b1 = fis.read(); int b2 = fis.read(); // Check for big/little endian unicode marker; otherwise use the default charset to open String targetCharset = defaultCharset; if (b1 == 0xFF && b2 == 0xFE) targetCharset = "UTF-16LE"; else if (b1 == 0xFE && b2 == 0xFF) targetCharset = "UTF-16BE"; else if (Sage.I18N_CHARSET.equals(defaultCharset)) { // Read 16k of data to verify that we have the proper charset if we think it's UTF8 byte[] extraData = new byte[16384]; extraData[0] = (byte)(b1 & 0xFF); extraData[1] = (byte)(b2 & 0xFF); int dataLen = 2 + fis.read(extraData, 2, extraData.length - 2); boolean utf8valid = true; for (int i = 0; i < dataLen && utf8valid; i++) { int c = extraData[i] & 0xFF; if (c <= 127) continue; if (i + 1 >= dataLen) { break; } switch (c >> 4) { case 12: case 13: /* 110x xxxx 10xx xxxx*/ i++; c = extraData[i] & 0xFF; if ((c & 0xC0) != 0x80) utf8valid = false; break; case 14: /* 1110 xxxx 10xx xxxx 10xx xxxx */ i++; c = extraData[i] & 0xFF; if ((c & 0xC0) != 0x80 || i + 1 >= dataLen) utf8valid = false; else { i++; c = extraData[i] & 0xFF; if ((c & 0xC0) != 0x80) utf8valid = false; } break; default: /* 10xx xxxx, 1111 xxxx */ utf8valid = false; break; } } if (!utf8valid) { if (Sage.DBG) System.out.println("Charset autodetection found invalid UTF8 data in the file; switching to default charset instead"); // Cp1252 is a superset of ISO-8859-1; so it's preferable to use it since it'll decode more characters...BUT we really should // just use the platform default instead; that's generally what people will be using from a text editor. // And embedded doesn't support Cp1252, so we need to remove that from there and just use ISO-8859-1 targetCharset = Sage.EMBEDDED ? Sage.BYTE_CHARSET : null; } } fis.close(); if (targetCharset == null) return new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(filePath))); else return new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(filePath), targetCharset)); } catch (java.io.IOException e) { if (fis != null) fis.close(); throw e; } } public static String calcMD5(java.io.File f) { if (f != null && f.isFile()) { java.io.FileInputStream fis = null; try { fis = new java.io.FileInputStream(f); java.security.MessageDigest algorithm = null; algorithm = java.security.MessageDigest.getInstance("MD5"); algorithm.reset(); byte[] buf = new byte[32768]; int numRead = fis.read(buf); while (numRead > 0) { algorithm.update(buf, 0, numRead); numRead = fis.read(buf); } byte[] digest = algorithm.digest(); StringBuffer finalSum = new StringBuffer(); for (int i = 0; i < digest.length; i++) { if (((int) (digest[i] & 0xFF)) <= 0x0F) { finalSum.append('0'); } finalSum.append(Integer.toHexString((int)(digest[i] & 0xFF))); } return finalSum.toString().toUpperCase(); } catch (Exception e) { /*if (Sage.DBG)*/ System.out.println("ERROR calculating MD5Sum of:" + e); return null; } finally { if (fis != null) { try { fis.close(); } catch (Exception e) {} } } } else return null; } public static final String[] VFAT_MOUNTABLE_PARreplacedION_TYPES = { "6", "b", "c", "e", "f" }; public static final String[] NTFS_MOUNTABLE_PARreplacedION_TYPES = { "7" }; public static boolean isExternalDriveMounted(String devPath, String mountPath) { return (IOUtils.exec2(new String[] { "sh", "-c", "mount | grep -i \"" + mountPath + " type\"" }) == 0); } public static boolean mountExternalDrive(String devPath, String mountPath) { return mountExternalDrive(devPath, mountPath, false); } public static boolean mountExternalDrive(String devPath, String mountPath, boolean optimizePerformance) { if (!Sage.LINUX_OS) return false; // Check to see if it's already mounted if (isExternalDriveMounted(devPath, mountPath)) { if (Sage.DBG) System.out.println("Ignoring mount for " + devPath + " because it's already mounted at: " + mountPath); return true; } // First use 'sfdisk' to determine the parreplacedion type so we know if we should // mount it w/ the '-o utf8' option or not String partNum = devPath.substring(devPath.length() - 1); String sfRes = IOUtils.exec(new String[] { "sfdisk", "-c", "/dev/" + devPath.substring(0, devPath.length() - 1), partNum}); sfRes = sfRes.trim(); if ("83".equals(sfRes) && optimizePerformance) { // Special options for mounting ext4 drives (this should fail if it's not ext4 since the 83 flag just means ext2/3/4) if (IOUtils.exec2(new String[] {"mount", "-t", "ext4", "/dev/" + devPath, mountPath, "-o", "noatime,barrier=0,data=writeback"}) == 0) return true; } // If we have a failure; then keep going and try all the possible ways to mount the drive. for (int i = 0; i < VFAT_MOUNTABLE_PARreplacedION_TYPES.length; i++) { if (sfRes.equals(VFAT_MOUNTABLE_PARreplacedION_TYPES[i])) { if (IOUtils.exec2(new String[] {"mount", "-t", "vfat", "/dev/" + devPath, mountPath, "-o", "utf8,noatime"}) == 0) return true; } } for (int i = 0; i < NTFS_MOUNTABLE_PARreplacedION_TYPES.length; i++) { if (sfRes.equals(NTFS_MOUNTABLE_PARreplacedION_TYPES[i])) { if (Sage.EMBEDDED) { if (IOUtils.exec2(new String[] {"/usr/local/bin/ntfs-3g", "/dev/" + devPath, mountPath, "-o", "nls=utf8,noatime"}) == 0) return true; } else if (IOUtils.exec2(new String[] {"mount", "/dev/" + devPath, mountPath, "-o", "nls=utf8,noatime"}) == 0) return true; } } if (devPath.length() == 3) { // This is for NTFS mounting since we mount the disk and not the parreplacedions if (Sage.EMBEDDED) { if (IOUtils.exec2(new String[] {"/usr/local/bin/ntfs-3g", "/dev/" + devPath, mountPath, "-o", "nls=utf8,noatime"}) == 0) return true; } else { if (IOUtils.exec2(new String[] {"mount", "/dev/" + devPath, mountPath, "-o", "nls=utf8,noatime"}) == 0) return true; } } if (IOUtils.exec2(new String[] {"mount", "/dev/" + devPath, mountPath, "-o", "noatime"}) == 0) return true; return (IOUtils.exec2(new String[] {"mount", "/dev/" + devPath, mountPath}) == 0); } // Reads a newline (\r\n or \n) terminated string (w/out returning the newline). rv can be used as a temp buffer, buf is the buffer used for reading the data and may already contain // extra data in it before calling this method, the SocketChannel preplaceded in MUST be a blocking socket. Extra data may be in the buf after returning from this method call. public static String readLineBytes(java.nio.channels.SocketChannel s, java.nio.ByteBuffer buf, long timeout, StringBuffer rv) throws java.io.InterruptedIOException, java.io.IOException { if (rv == null) rv = new StringBuffer(); else rv.setLength(0); boolean needsFlip = true; if (buf.hasRemaining() && buf.position() > 0) needsFlip = false; else buf.clear(); int readRes = 0; TimeoutHandler.registerTimeout(timeout, s); try { if ((!buf.hasRemaining() || buf.position() == 0) && (readRes = s.read(buf)) <= 0) { throw new java.io.EOFException(); } } finally { TimeoutHandler.clearTimeout(s); } if (needsFlip) buf.flip(); int currByte = (buf.get() & 0xFF); while (true) { if (currByte == '\r') { if (!buf.hasRemaining()) { buf.clear(); TimeoutHandler.registerTimeout(timeout, s); try { if ((readRes = s.read(buf)) <= 0) { throw new java.io.EOFException(); } } finally { TimeoutHandler.clearTimeout(s); } buf.flip(); } currByte = (buf.get() & 0xFF); if (currByte == '\n') { return rv.toString(); } rv.append('\r'); } else if (currByte == '\n') { return rv.toString(); } rv.append((char)currByte); if (!buf.hasRemaining()) { buf.clear(); TimeoutHandler.registerTimeout(timeout, s); try { if ((readRes = s.read(buf)) <= 0) { throw new java.io.EOFException(); } } finally { TimeoutHandler.clearTimeout(s); } buf.flip(); } currByte = (buf.get() & 0xFF); } } // Downloads all of the specified files from a SageTV server to the target destination files. Only allowed with // SageTVClient mode public static void downloadFilesFromSageTVServer(java.io.File[] srcFiles, java.io.File[] dstFiles) throws java.io.IOException { if (!Sage.client) throw new java.io.IOException("Cannot download files from SageTV server since we are not in client mode!"); java.net.Socket sock = null;; java.io.DataOutputStream outStream = null; java.io.DataInputStream inStream = null; java.io.OutputStream fileOut = null; try { sock = new java.net.Socket(); sock.connect(new java.net.InetSocketAddress(Sage.preferredServer, 7818), 5000); sock.setSoTimeout(30000); //sock.setTcpNoDelay(true); outStream = new java.io.DataOutputStream(new java.io.BufferedOutputStream(sock.getOutputStream())); inStream = new java.io.DataInputStream(new java.io.BufferedInputStream(sock.getInputStream())); byte[] xferBuf = new byte[32768]; for (int i = 0; i < srcFiles.length; i++) { // Always request file access since this is generally not used for MediaFile objects NetworkClient.getSN().requestMediaServerAccess(srcFiles[i], true); outStream.write("OPENW ".getBytes(Sage.BYTE_CHARSET)); outStream.write(srcFiles[i].toString().getBytes("UTF-16BE")); outStream.write("\r\n".getBytes(Sage.BYTE_CHARSET)); outStream.flush(); String str = Sage.readLineBytes(inStream); if (!"OK".equals(str)) throw new java.io.IOException("Error opening remote file of:" + str); // get the size outStream.write("SIZE\r\n".getBytes(Sage.BYTE_CHARSET)); outStream.flush(); str = Sage.readLineBytes(inStream); long remoteSize = Long.parseLong(str.substring(0, str.indexOf(' '))); fileOut = new java.io.FileOutputStream(dstFiles[i]); outStream.write(("READ 0 " + remoteSize + "\r\n").getBytes(Sage.BYTE_CHARSET)); outStream.flush(); while (remoteSize > 0) { int currRead = (int)Math.min(xferBuf.length, remoteSize); inStream.readFully(xferBuf, 0, currRead); fileOut.write(xferBuf, 0, currRead); remoteSize -= currRead; } fileOut.close(); fileOut = null; // CLOSE happens automatically when you open a new file //outStream.write("CLOSE\r\n".getBytes(Sage.BYTE_CHARSET)); //outStream.flush(); NetworkClient.getSN().requestMediaServerAccess(srcFiles[i], false); } } finally { try{ if (sock != null) sock.close(); }catch (Exception e1){} try{ if (outStream != null) outStream.close(); }catch (Exception e2){} try{ if (inStream != null) inStream.close(); }catch (Exception e3){} try{ if (fileOut != null) fileOut.close(); }catch (Exception e4){} } } // This uses the old MVP server which as part of the protocol will return the MAC address of the other system public static String getServerMacAddress(String hostname) { // Strip off any port that may be there if (hostname.indexOf(":") != -1) hostname = hostname.substring(0, hostname.indexOf(":")); if (Sage.DBG) System.out.println("Requested to find MAC address of server at: " + hostname); java.net.DatagramSocket sock = null; try { sock = new java.net.DatagramSocket(16882); // they always come back on this port java.net.DatagramPacket pack = new java.net.DatagramPacket(new byte[50], 50); byte[] data = pack.getData(); data[3] = 1; String localIP; if (Sage.WINDOWS_OS || Sage.MAC_OS_X) localIP = java.net.InetAddress.getLocalHost().getHostAddress(); else localIP = LinuxUtils.getIPAddress(); if (Sage.DBG) System.out.println("Local IP is " + localIP); int idx = localIP.indexOf('.'); data[16] = (byte)(Integer.parseInt(localIP.substring(0, idx)) & 0xFF); localIP = localIP.substring(idx + 1); idx = localIP.indexOf('.'); data[17] = (byte)(Integer.parseInt(localIP.substring(0, idx)) & 0xFF); localIP = localIP.substring(idx + 1); idx = localIP.indexOf('.'); data[18] = (byte)(Integer.parseInt(localIP.substring(0, idx)) & 0xFF); localIP = localIP.substring(idx + 1); data[19] = (byte)(Integer.parseInt(localIP) & 0xFF); pack.setLength(50); // Find the broadcast address for this subnet. pack.setAddress(java.net.InetAddress.getByName(hostname)); pack.setPort(16881); sock.send(pack); sock.setSoTimeout(3000); sock.receive(pack); if (pack.getLength() >= 20) { if (Sage.DBG) System.out.println("MAC discovery packet received:" + pack); String mac = (((data[8] & 0xFF) < 16) ? "0" : "") + Integer.toString(data[8] & 0xFF, 16) + ":" + (((data[9] & 0xFF) < 16) ? "0" : "") + Integer.toString(data[9] & 0xFF, 16) + ":" + (((data[10] & 0xFF) < 16) ? "0" : "") + Integer.toString(data[10] & 0xFF, 16) + ":" + (((data[11] & 0xFF) < 16) ? "0" : "") + Integer.toString(data[11] & 0xFF, 16) + ":" + (((data[12] & 0xFF) < 16) ? "0" : "") + Integer.toString(data[12] & 0xFF, 16) + ":" + (((data[13] & 0xFF) < 16) ? "0" : "") + Integer.toString(data[13] & 0xFF, 16); if (Sage.DBG) System.out.println("Resulting MAC=" + mac); return mac; } } catch (Exception e) { //System.out.println("Error discovering servers:" + e); } finally { if (sock != null) { try { sock.close(); }catch (Exception e){} sock = null; } } return null; } public static void sendWOLPacket(String macAddress) { // Strip off any port that may be there if (Sage.DBG) System.out.println("Sending out WOL packet to MAC address: " + macAddress); java.net.DatagramSocket sock = null; try { sock = new java.net.DatagramSocket(9); // WOL is on port 9 java.net.DatagramPacket pack = new java.net.DatagramPacket(new byte[102], 102); byte[] data = pack.getData(); data[0] = (byte)0xFF; data[1] = (byte)0xFF; data[2] = (byte)0xFF; data[3] = (byte)0xFF; data[4] = (byte)0xFF; data[5] = (byte)0xFF; byte[] macBytes = new byte[6]; java.util.StringTokenizer macToker = new java.util.StringTokenizer(macAddress, ":-"); for (int i = 0; i < macBytes.length; i++) { macBytes[i] = (byte)(Integer.parseInt(macToker.nextToken(), 16) & 0xFF); } for (int i = 0; i < 16; i++) { System.arraycopy(macBytes, 0, data, 6*(i + 1), 6); } String localIP; if (Sage.WINDOWS_OS || Sage.MAC_OS_X) localIP = java.net.InetAddress.getLocalHost().getHostAddress(); else localIP = LinuxUtils.getIPAddress(); if (Sage.DBG) System.out.println("Local IP is " + localIP); int lastDot = localIP.lastIndexOf('.'); String broadcastIP = localIP.substring(0, lastDot) + ".255"; if (Sage.DBG) System.out.println("Broadcast IP is " + broadcastIP); pack.setLength(102); pack.setAddress(java.net.InetAddress.getByName(broadcastIP)); pack.setPort(9); sock.send(pack); } catch (Exception e) { //System.out.println("Error discovering servers:" + e); } finally { if (sock != null) { try { sock.close(); }catch (Exception e){} sock = null; } } } public static byte[] getFileAsBytes(java.io.File f) { try { java.io.InputStream is = new java.io.FileInputStream(f); byte[] rv = new byte[(int)f.length()]; int numRead = is.read(rv); while (numRead < rv.length) { int currRead = is.read(rv, numRead, rv.length - numRead); if (currRead < 0) break; numRead += currRead; } is.close(); return rv; } catch (java.io.IOException e) { if (Sage.DBG) System.out.println("ERROR reading file " + f + " of:" + e); return null; } } }