package ca.canuckcoding.webos; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.ResourceBundle; import javax.swing.JOptionPane; import org.json.JSONObject; /** * @author Jason Robitaille */ public abstract class WebOSConnection { public final String offlineRoot = "/media/cryptofs/apps"; public boolean javaRestartFlag; public boolean lunaRestartFlag; public boolean deviceRestartFlag; protected ResourceBundle locale; private DeviceInfo info; private WebOSDevice device; private String pkgMgr = "ipkg"; public WebOSConnection(WebOSDevice wd) { javaRestartFlag = false; lunaRestartFlag = false; deviceRestartFlag = false; locale = ResourceBundle.getBundle("ca/canuckcoding/webos/Locale"); info = null; device = wd; } public abstract boolean isConnected(); public abstract boolean sendFile(File src, String dest); public abstract boolean receiveFile(String src, File dest); public File receiveFile(String src) { File result = null; String tmpFilePath = System.getProperty("java.io.tmpdir"); File tmp = new File(tmpFilePath, getFileName(src)); if(tmp.exists()) { tmp.delete(); } if(receiveFile(src, tmp)) { result = tmp; } return result; } public abstract String runProgram(String app, String[] params) throws WebOSException; public abstract List<JSONObject> lunaSend(String address, JSONObject params) throws WebOSException; public boolean remountPartitionReadWrite() { boolean result = false; try { if(!getDeviceInfo().buildName().contains("SDK")) { runProgram("/bin/mount", new String[] {"-o", "remount,rw", "/"}); } result = true; } catch(Exception e) {} return result; } public boolean fileExists(String file) { boolean result = false; try { runProgram("/bin/ls", new String[] {"-d", file}); result = true; } catch(Exception e) {} return result; } public boolean mkdir(String path) { boolean result = false; try { runProgram("/bin/mkdir", new String[] {"-p", path}); result = true; } catch(Exception e) { e.printStackTrace(); } return result; } public boolean delete(String path) { boolean result = false; try { runProgram("/bin/rm", new String[] {"-fr", path}); result = true; } catch(Exception e) { e.printStackTrace(); } return result; } public DeviceInfo getDeviceInfo() { if(info==null) { info = new DeviceInfo(this, device); } return info; } public String getPkgMgr() { return pkgMgr; } /* Old ipkg-based version public ArrayList<InstalledEntry> listInstalled() { ArrayList<InstalledEntry> apps = new ArrayList(); try { String stdout = runProgram("/usr/bin/ipkg", new String[] {"-o", offlineRoot, "list_installed"}); String[] lines = stdout.split("\n"); for(int i=0; i<lines.length; i++) { lines[i] = lines[i].trim(); if(lines[i].length()>0) { InstalledEntry curr = new InstalledEntry(lines[i]); if(curr!=null && curr.getName()!=null) { apps.add(curr); } } } } catch(Exception e) { e.printStackTrace(); } return apps; }*/ public ArrayList<InstalledEntry> listInstalled() { ArrayList<InstalledEntry> apps = new ArrayList(); try { String status = offlineRoot + "/usr/lib/ipkg/status"; if(pkgMgr.equals("opkg")) { status = offlineRoot + "/var/lib/opkg/status"; } if(fileExists(status)) { String stdout = runProgram("/bin/grep", new String[] {"-e", "Package:", "-e", "Version:", "-e", "Description:", status}); //"-e", "^$" String[] lines = stdout.split("\n"); String id = "<unknown id>"; String version = "?.?.?"; String name = "Unknown"; boolean pending = false; for(int i=0; i<lines.length; i++) { String val = lines[i].substring(lines[i].indexOf(":")+1).trim(); if(lines[i].startsWith("Package:")) { if(i>0 && i!=lines.length-1) { apps.add(new InstalledEntry(name, id, version)); version = "?.?.?"; name = "Unknown"; } id = val; pending = true; } else if(lines[i].startsWith("Version:")) { version = val; } else if(lines[i].startsWith("Description:")) { name = val; } } if(pending) { apps.add(new InstalledEntry(name, id, version)); } } } catch(Exception e) {} return apps; } public ArrayList<String> whatDepends(String appid) { ArrayList<String> apps = new ArrayList(); try { String stdout = runProgram("/usr/bin/" + pkgMgr, new String[] {"-o", offlineRoot, "whatdepends", appid}); String[] lines = stdout.split("\n"); for(int i=3; i<lines.length; i++) { String[] tokens = lines[i].trim().split("\\s+"); if(tokens[tokens.length-1].equals(appid)) { apps.add(tokens[0]); } } } catch(Exception e) { e.printStackTrace(); } return apps; } public ArrayList<String> whatDependsRecursive(String appid) { ArrayList<String> apps = new ArrayList(); try { String stdout = runProgram("/usr/bin/" + pkgMgr, new String[] {"-o", offlineRoot, "whatdependsrec", appid}); String[] lines = stdout.split("\n"); for(int i=3; i<lines.length; i++) { lines[i] = lines[i].trim(); apps.add(lines[i].substring(0, lines[i].indexOf(" "))); } } catch(Exception e) { e.printStackTrace(); } return apps; } public boolean sendScript(ScriptType type) { Script scriptRes = new Script(type); File script = scriptRes.extract(); boolean result = false; if(script!=null) { if(sendFile(script, "/var/" + type.filename())) { result = true; } else { System.err.println("ERROR: Unable to copy " + type.filename() + " to device"); } } else { System.err.println("ERROR: " + type.filename() + " could not be extracted"); } return result; } public boolean removeScript(ScriptType type) { boolean result = false; try { runProgram("/bin/rm", new String[] {"-f", "/var/" + type.filename()}); result = true; } catch(Exception e) { e.printStackTrace(); } return result; } private String getFileName(String path) { String result = path; int index = result.lastIndexOf("/"); if(index!=-1) { result = result.substring(index+1); } index = result.lastIndexOf("\\"); if(index!=-1) { result = result.substring(index+1); } return result; } public boolean isSystemBusy() { boolean result = false; if(!fileExists("/usr/bin/ipkg")) { pkgMgr = "opkg"; } if(pkgMgr.equals("ipkg")) { try { String out = runProgram("/usr/bin/pgrep", new String[] {"-f", "ApplicationInstallerUtility"}); if(!out.trim().isEmpty()) { result = true; } } catch(Exception e) {} } return result; } public void configPkgMgr() { if(!fileExists("/usr/bin/ipkg")) { pkgMgr = "opkg"; } if(!fileExists("/media/cryptofs/apps/etc/" + pkgMgr + "/arch.conf")) { try { mkdir("/media/cryptofs/apps/etc/" + pkgMgr); runProgram("/bin/cp", new String[]{"-f", "/etc/" + pkgMgr + "/arch.conf", "/media/cryptofs/apps/etc/" + pkgMgr + "/arch.conf"}); } catch(Exception e) { System.err.println("Unable to copy " + pkgMgr + " configuration file:" + e.getMessage()); } } } public boolean install(File file) { boolean result = false; String name = getFileName(file.getName()); if(mkdir("/media/internal/.developer")) { if(sendFile(file, "/media/internal/.developer/" + name)) { result = install("/media/internal/.developer/" + name); } } return result; } public boolean install(String filename) { boolean result = false; try { String out = runProgram("/bin/sh", new String[] {"/var/" + ScriptType.ScanID.filename(), filename}); String[] tokens = out.split("\n"); String appid = null, restartFlag = null, arch = null; String[] depends = new String[0]; for(int i=0; i<tokens.length; i++) { if(tokens[i].startsWith("Package:")) { appid = tokens[i].substring(tokens[i].indexOf(":")+1).trim(); } else if(tokens[i].startsWith("Architecture:")) { arch = tokens[i].substring(tokens[i].indexOf(":")+1).trim(); } else if(tokens[i].startsWith("Depends:")) { depends = tokens[i].substring(tokens[i].indexOf(":")+1).split(","); } else if(tokens[i].startsWith("Source:")) { try { JSONObject source = new JSONObject(tokens[1] .substring(tokens[1].indexOf("{"), tokens[1].lastIndexOf("}")+1)); restartFlag = source.getString("PostInstallFlags").toLowerCase(); } catch(Exception e) {} } } try { if(arch!=null && !arch.equals("any") && !arch.equals("all") && !getDeviceInfo().arch().startsWith(arch)) { throw new WebOSException(locale.getString("IPKG_INSTALL_FAILED_ARCH")); } ArrayList<InstalledEntry> installed = listInstalled(); for(int i=0; i<depends.length; i++) { depends[i] = depends[i].trim(); if(depends[i].length()!=0) { if(!installed.contains(new InstalledEntry(depends[i]))) { throw new WebOSException(locale.getString("IPKG_INSTALL_FAILED_DEPENDS")); } } } JSONObject params = new JSONObject(); params.put("target", filename); params.put("subscribe", true); params.put("uncompressedSize", 0); lunaSend("palm://com.palm.appinstaller/installNoVerify", params); String infoDir = offlineRoot + "/usr/lib/ipkg/info/"; if(pkgMgr.equals("opkg")) { infoDir = offlineRoot + "/var/lib/opkg/info/"; } if(fileExists(infoDir + appid + ".control")) { if(fileExists(infoDir + appid + ".postinst")) { out = runIpkgScript(infoDir + appid + ".postinst"); } result = true; handleRestartFlag(restartFlag); } else { throw new WebOSException(locale.getString("IPKG_INSTALL_FAILED")); } } catch(Exception e) { JOptionPane.showMessageDialog(null, MessageFormat.format( locale.getString("ERROR:_AN_ERROR_OCCURED_WHILE_ATTEMPTING_TO_INSTALL_{0}"), new Object[] {getFileName(filename)}) + "\n\n" + e.getMessage()); System.err.println("Unable to install " + getFileName(filename)); } } catch(Exception e) { JOptionPane.showMessageDialog(null, locale.getString("ERROR:_INVALID_OR_CORRUPT_PACKAGE") + "\n" + getFileName(filename)); } return result; } public boolean uninstall(String appid) { boolean result = false; try { String out; String restartFlag = ""; String infoDir = offlineRoot + "/usr/lib/ipkg/info/"; if(pkgMgr.equals("opkg")) { infoDir = offlineRoot + "/var/lib/opkg/info/"; } try { out = runProgram("/bin/grep", new String[] {"Source:", infoDir + appid + ".control"}); JSONObject source = new JSONObject(out.substring(out.indexOf("{"), out.lastIndexOf("}")+1)); restartFlag = source.getString("PostRemoveFlags").toLowerCase(); } catch(Exception e) {} if(fileExists(infoDir + appid + ".prerm")) { out = runIpkgScript(infoDir + appid + ".prerm"); } if(fileExists(offlineRoot + "/usr/palm/applications/" + appid + "/appinfo.json")) { JSONObject params = new JSONObject(); params.put("packageName", appid); params.put("subscribe", true); lunaSend("palm://com.palm.appinstaller/remove", params); } else { try { out = runProgram("/bin/sh", new String[] {offlineRoot + "/.scripts/" + appid + "/pmPreRemove.script"}); } catch(Exception e) {} try { out = runProgram("/usr/bin/" + pkgMgr, new String[] {"-o", offlineRoot, "-force-depends", "remove", appid}); } catch(Exception e) { throw new WebOSException(locale.getString("IPKG_UNINSTALL_FAILED")); } } if(!fileExists(infoDir + appid + ".control")) { result = true; if(appid.startsWith("ca.canucksoftware.patches.") || appid.startsWith("ca.canuckcoding.patches.")) { lunaRestartFlag = true; } else { handleRestartFlag(restartFlag); } } else { throw new WebOSException(locale.getString("IPKG_UNINSTALL_FAILED")); } } catch(Exception e) { JOptionPane.showMessageDialog(null, MessageFormat.format( locale.getString("ERROR:_AN_ERROR_OCCURED_WHILE_ATTEMPTING_TO_UNINSTALL_{0}"), new Object[] {appid}) + "\n\n" + e.getMessage()); e.printStackTrace(); } return result; } private String runIpkgScript(String file) throws WebOSException { File script = new File(System.getProperty("java.io.tmpdir"), "ipkgScript"); try { BufferedWriter bw = new BufferedWriter(new FileWriter(script)); bw.write("/bin/sh -c 'export IPKG_OFFLINE_ROOT=" + offlineRoot + " ; /bin/sh " + file + "'\n"); bw.flush(); bw.close(); sendFile(script, "/tmp/ipkgScript.sh"); script.delete(); } catch(IOException e) { throw new WebOSException(e); } return runProgram("/bin/sh", new String[] {"/tmp/ipkgScript.sh"}); } private void handleRestartFlag(String flag) { if(flag!=null) { if(flag.length()>0) { javaRestartFlag |= flag.equals("restartjava"); lunaRestartFlag |= flag.equals("restartluna"); deviceRestartFlag |= flag.equals("restartdevice"); } } } public void executeRestartFlags() { if(deviceRestartFlag) { deviceRestart(); } else { if(javaRestartFlag) { javaRestart(); } if(lunaRestartFlag) { lunaRestart(); } } javaRestartFlag = false; lunaRestartFlag = false; deviceRestartFlag = false; } public void javaRestart() { if(getDeviceInfo().version().charAt(0)=='1') { try { runProgram("/usr/bin/killall", new String[] {"-9", "java"}); } catch(Exception e) { System.err.println("Error while attemptong to Java restart: " + e.getMessage()); } } } public void lunaRestart() { try { runProgram("/usr/bin/killall", new String[] {"-HUP", "LunaSysMgr"}); } catch(Exception e) { System.err.println("Error while attemptong to Java restart: " + e.getMessage()); } } public void deviceRestart() { try { runProgram("/sbin/reboot", new String[] {}); } catch(Exception e) { System.err.println("Error while attemptong to Java restart: " + e.getMessage()); } } public abstract void launchTerminal() throws UnsupportedOperationException; }