Skip to content

Instantly share code, notes, and snippets.

@chrox
Last active October 13, 2015 12:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrox/4193209 to your computer and use it in GitHub Desktop.
Save chrox/4193209 to your computer and use it in GitHub Desktop.
Just another KPV launcher
package com.mobileread.chrox.patch.kpw.kpvlauncher;
import java.io.PrintStream;
import java.security.AllPermission;
import java.security.Permission;
import java.util.Date;
import java.util.Map;
import java.util.Arrays;
import java.net.URI;
import serp.bytecode.BCClass;
import serp.bytecode.Code;
import serp.bytecode.Instruction;
import org.json.simple.JSONObject;
import org.json.simple.JSONArray;
import com.amazon.kindle.restricted.runtime.Framework;
import com.amazon.kindle.restricted.content.catalog.ContentCatalog;
import com.mobileread.ixtab.jbpatch.Log;
import com.mobileread.ixtab.jbpatch.Patch;
import com.mobileread.ixtab.jbpatch.PatchMetadata;
import com.mobileread.ixtab.jbpatch.PatchMetadata.PatchableClass;
public class BookletPatch extends Patch {
// located at /opt/amazon/ebook/lib/kaf.jar
private static final String CLASS_BOOKLETCALLER_530 = "com.amazon.kindle.restricted.booklet.impl.S";
public static final String MD5_BOOKLETCALLER_530_BEFORE = "b584b11d2a9f771398bc4def8e7a5f0e";
public static final String MD5_BOOKLETCALLER__530_AFTER = "e01e0d69b57991f62196bacca98da4b2";
// located at /opt/amazon/ebook/booklet/Reader.jar
private static final String CLASS_BOOKLETSTARTER_530 = "com.amazon.ebook.booklet.reader.ReaderBooklet";
public static final String MD5_BOOKLETSTARTER_530_BEFORE = "571f2eee300f932c3f7419108ea969de";
public static final String MD5_BOOKLETSTARTER_530_AFTER = "dcd0431dbded7e07c38aec2688364b92";
// located at /opt/amazon/ebook/lib/kafindexer.jar
private static final String CLASS_INDEXERBUNDLE_530 = "com.amazon.kindle.indexer.bundle.IndexerBundle";
public static final String MD5_INDEXERBUNDLE_530_BEFORE = "632a6f3b66aeefb84ad63a479ab2b5e3";
public static final String MD5_INDEXERBUNDLE_530_AFTER = "dcd0431dbded7e07c38aec2688364b92";
private static final PrintStream log = Log.INSTANCE;
private static final String[] doc_types = {"pdf","djvu","epub","cbz","chm","doc","fb2","htm","html","pdb","rtf","tcr","xps","zip"};
public int getVersion() {
return 20121204;
}
public Permission[] getRequiredPermissions() {
return new Permission[] {new AllPermission()};
}
protected void initLocalization(String locale, Map map) {
if (RESOURCE_ID_ENGLISH.equals(locale)) {
map.put(I18N_JBPATCH_NAME, "Substitude pdf viewer with KPV");
map.put(I18N_JBPATCH_DESCRIPTION, "This patch allows to use KPV to read PDF documents.");
}
}
public PatchMetadata getMetadata() {
return new PatchMetadata(this).withClass(
new PatchableClass(CLASS_BOOKLETCALLER_530).withChecksums(
MD5_BOOKLETCALLER_530_BEFORE, MD5_BOOKLETCALLER__530_AFTER)).withClass(
new PatchableClass(CLASS_BOOKLETSTARTER_530).withChecksums(
MD5_BOOKLETSTARTER_530_BEFORE, MD5_BOOKLETSTARTER_530_AFTER)).withClass(
new PatchableClass(CLASS_INDEXERBUNDLE_530).withChecksums(
MD5_INDEXERBUNDLE_530_BEFORE, MD5_INDEXERBUNDLE_530_AFTER));
}
public String perform(String md5, BCClass clazz) throws Throwable {
if (md5.equals(MD5_BOOKLETCALLER_530_BEFORE)) {
return patchMethodBookletCaller(clazz);
}
if (md5.equals(MD5_BOOKLETSTARTER_530_BEFORE)) {
return patchMethodBookletStarter(clazz);
}
/* // for debug use
if (md5.equals(MD5_INDEXERBUNDLE_530_BEFORE)) {
return patchMethodCallCC(clazz);
}*/
return "unsupported MD5: "+md5;
}
private void sanitize(Code c) {
c.calculateMaxLocals();
c.calculateMaxStack();
}
private String patchMethodBookletCaller(BCClass clazz) throws Exception {
Code c = clazz.getDeclaredMethod("DxA").getCode(false);
//dump(c);
c.afterLast();
Instruction ret = c.previous();
//c.beforeFirst();
c.before(102);
c.aload().setLocal(1);
c.invokestatic().setMethod(BookletPatch.class.getDeclaredMethod("bypassCaller", new Class[] {String.class}));
c.ifeq().setTarget(ret);
//dump(c);
sanitize(c);
return null;
}
private String patchMethodBookletStarter(BCClass clazz) throws Exception {
Code c = clazz.getDeclaredMethod("start").getCode(false);
//dump(c);
c.afterLast();
Instruction ret = c.previous();
c.beforeFirst();
c.aload().setLocal(1);
c.invokestatic().setMethod(BookletPatch.class.getDeclaredMethod("startKPV", new Class[] {URI.class}));
c.ifeq().setTarget(ret);
//dump(c);
sanitize(c);
return null;
}
private String patchMethodCallCC(BCClass clazz) throws Exception {
Code c = clazz.getDeclaredMethod("eL").getCode(false);
//dump(c);
c.before(220);
c.aload().setLocal(1);
c.aload().setLocal(2);
c.aload().setLocal(11);
c.invokestatic().setMethod(BookletPatch.class.getDeclaredMethod("ccRequestLog", new Class[] {String.class, String.class, JSONObject.class}));
//dump(c);
sanitize(c);
return null;
}
public static int bypassCaller(String str) {
log("Booklet is called with URI: " + str);
String path = "";
try {
path = new URI(str).getPath();
} catch (Exception e) {
log("E: while parsing Path in " + str + ": ");
e.printStackTrace(log);
return -1;
}
int dot = path.lastIndexOf('.');
String ext = (dot == -1) ? "" : path.substring(dot+1).toLowerCase();
if (Arrays.asList(doc_types).contains(ext)) {
log("Bypassing booklet caller on " + path);
return 0;
}
return -1;
}
public static int startKPV(URI uri) {
String path = uri.getPath();
log("Booklet is started with doc: " + path);
int dot = path.lastIndexOf('.');
String ext = (dot == -1) ? "" : path.substring(dot+1).toLowerCase();
if (Arrays.asList(doc_types).contains(ext)) {
long lastAccess = new Date().getTime() / 1000L;
updateCC(path, ext.toUpperCase(), lastAccess, 0.0f);
log("Opening " + path + " with kindlepdfviewer...");
String[] cmd = new String[] {"/mnt/us/kindlepdfviewer/kpdf.sh", path};
try {
// Run kpdf command
Runtime.getRuntime().exec(cmd);
//throw new RuntimeException();
} catch (Exception e) {
e.printStackTrace(log);
}
return 0;
}
return -1;
}
public static void updateCC(String path, String tag, long lastAccess, float percentFinished) {
path = JSONObject.escape(path);
String json_query = "{\"filter\":{\"Equals\":{\"value\":\"" + path + "\",\"path\":\"location\"}},\"type\":\"QueryRequest\",\"maxResults\":1,\"sortOrder\":[{\"order\":\"descending\",\"path\":\"lastAccess\"},{\"order\":\"ascending\",\"path\":\"titles[0].collation\"}],\"startIndex\":0,\"id\":1,\"resultType\":\"fast\"}";
JSONObject json = CCRequest("query", json_query);
JSONArray values = (JSONArray) json.get("values");
JSONObject value =(JSONObject) values.get(0);
String uuid = (String) value.get("uuid");
String json_change = "{\"commands\":[{\"update\":{\"uuid\":\"" + uuid + "\",\"lastAccess\":" + lastAccess + ",\"percentFinished\":" + percentFinished + ",\"displayTags\":[\"" + tag + "\"]" + "}}],\"type\":\"ChangeRequest\",\"id\":1}";
CCRequest("change", json_change);
}
public static JSONObject CCRequest(String req_type, String req_json) {
ContentCatalog CC = (ContentCatalog)Framework.getService(ContentCatalog.class);
JSONObject json = CC.eL(req_type, req_json, 200, 5);
//log("CCRequest:type:" + req_type +",json=" + req_json);
//log("*************************************");
//log("CCReturn:json=" + json.toJSONString());
return json;
}
public static void ccRequestLog(String type, String cal_json, JSONObject ret_json) {
log("CCRequest:type:" + type +",json=" + cal_json);
log(".....................................");
log("CCReturn:json=" + ret_json);
/*try {
throw new RuntimeException();
} catch (Exception e) {
e.printStackTrace(log);
}*/
}
}
#!/bin/sh
export LC_ALL="en_US.UTF-8"
echo unlock > /proc/keypad
echo unlock > /proc/fiveway
# we're always starting from our working directory
cd /mnt/us/kindlepdfviewer/
# bind-mount system fonts
if ! grep /mnt/us/kindlepdfviewer/fonts/host /proc/mounts; then
mount -o bind /usr/java/lib/fonts /mnt/us/kindlepdfviewer/fonts/host
fi
# check if we are supposed to shut down the Amazon framework
if test "$1" == "--framework_stop"; then
shift 1
/etc/init.d/framework stop
fi
# stop cvm
killall -stop cvm
# finally call reader
./reader.lua "$1" 2> crash.log
# unmount system fonts
if grep /mnt/us/kindlepdfviewer/fonts/host /proc/mounts; then
umount /mnt/us/kindlepdfviewer/fonts/host
fi
# always try to continue cvm
killall -cont cvm || restart framework
# return to home screen
lipc-set-prop com.lab126.appmgrd start app://com.lab126.booklet.home
INSERT INTO "properties" VALUES('com.lab126.booklet.kpvbooklet','lipcId','com.lab126.booklet.kpvbooklet');
INSERT INTO "properties" VALUES('com.lab126.booklet.kpvbooklet','jar','/opt/amazon/ebook/booklet/KPVBooklet.jar');
INSERT INTO "mimetypes" VALUES('pdf','MT:application/pdf');
INSERT INTO "extenstions" VALUES('pdf','MT:application/pdf');
INSERT INTO "properties" VALUES('archive.displaytags.mimetypes','application/pdf','PDF');
INSERT INTO "associations" VALUES('com.lab126.booklet.kpvbooklet','application','MT:application/pdf','true');
INSERT INTO "associations" VALUES('com.lab126.generic.extractor','extractor','GL:*.pdf','true');
INSERT INTO "mimetypes" VALUES('djvu','MT:image/x.djvu');
INSERT INTO "extenstions" VALUES('djvu','MT:image/x.djvu');
INSERT INTO "properties" VALUES('archive.displaytags.mimetypes','image/x.djvu','DjVu');
INSERT INTO "associations" VALUES('com.lab126.booklet.kpvbooklet','application','MT:image/x.djvu','true');
INSERT INTO "associations" VALUES('com.lab126.generic.extractor','extractor','GL:*.djvu','true');
INSERT INTO "mimetypes" VALUES('epub','MT:application/epub+zip');
INSERT INTO "extenstions" VALUES('epub','MT:application/epub+zip');
INSERT INTO "properties" VALUES('archive.displaytags.mimetypes','application/epub+zip','EPUB');
INSERT INTO "associations" VALUES('com.lab126.booklet.kpvbooklet','application','MT:application/epub+zip','true');
INSERT INTO "associations" VALUES('com.lab126.generic.extractor','extractor','GL:*.epub','true');
INSERT INTO "mimetypes" VALUES('cbz','MT:application/cbz');
INSERT INTO "extenstions" VALUES('cbz','MT:application/cbz');
INSERT INTO "properties" VALUES('archive.displaytags.mimetypes','application/cbz','CBZ');
INSERT INTO "associations" VALUES('com.lab126.booklet.kpvbooklet','application','MT:application/cbz','true');
INSERT INTO "associations" VALUES('com.lab126.generic.extractor','extractor','GL:*.cbz','true');
INSERT INTO "mimetypes" VALUES('chm','MT:application/chm');
INSERT INTO "extenstions" VALUES('chm','MT:application/chm');
INSERT INTO "properties" VALUES('archive.displaytags.mimetypes','application/chm','CHM');
INSERT INTO "associations" VALUES('com.lab126.booklet.kpvbooklet','application','MT:application/chm','true');
INSERT INTO "associations" VALUES('com.lab126.generic.extractor','extractor','GL:*.chm','true');
INSERT INTO "mimetypes" VALUES('doc','MT:application/doc');
INSERT INTO "extenstions" VALUES('doc','MT:application/doc');
INSERT INTO "properties" VALUES('archive.displaytags.mimetypes','application/doc','DOC');
INSERT INTO "associations" VALUES('com.lab126.booklet.kpvbooklet','application','MT:application/doc','true');
INSERT INTO "associations" VALUES('com.lab126.generic.extractor','extractor','GL:*.doc','true');
INSERT INTO "mimetypes" VALUES('fb2','MT:application/fb2');
INSERT INTO "extenstions" VALUES('fb2','MT:application/fb2');
INSERT INTO "properties" VALUES('archive.displaytags.mimetypes','application/fb2','FB2');
INSERT INTO "associations" VALUES('com.lab126.booklet.kpvbooklet','application','MT:application/fb2','true');
INSERT INTO "associations" VALUES('com.lab126.generic.extractor','extractor','GL:*.fb2','true');
INSERT INTO "mimetypes" VALUES('htm','MT:text/htm');
INSERT INTO "extenstions" VALUES('htm','MT:text/htm');
INSERT INTO "properties" VALUES('archive.displaytags.mimetypes','text/htm','HTML');
INSERT INTO "associations" VALUES('com.lab126.booklet.kpvbooklet','application','MT:text/htm','true');
INSERT INTO "associations" VALUES('com.lab126.generic.extractor','extractor','GL:*.htm','true');
INSERT INTO "mimetypes" VALUES('html','MT:text/html');
INSERT INTO "extenstions" VALUES('html','MT:text/html');
INSERT INTO "properties" VALUES('archive.displaytags.mimetypes','text/html','HTML');
INSERT INTO "associations" VALUES('com.lab126.booklet.kpvbooklet','application','MT:text/html','true');
INSERT INTO "associations" VALUES('com.lab126.generic.extractor','extractor','GL:*.html','true');
INSERT INTO "mimetypes" VALUES('pdb','MT:application/pdb');
INSERT INTO "extenstions" VALUES('pdb','MT:application/pdb');
INSERT INTO "properties" VALUES('archive.displaytags.mimetypes','application/pdb','PDB');
INSERT INTO "associations" VALUES('com.lab126.booklet.kpvbooklet','application','MT:application/pdb','true');
INSERT INTO "associations" VALUES('com.lab126.generic.extractor','extractor','GL:*.pdb','true');
INSERT INTO "mimetypes" VALUES('rtf','MT:application/rtf');
INSERT INTO "extenstions" VALUES('rtf','MT:application/rtf');
INSERT INTO "properties" VALUES('archive.displaytags.mimetypes','application/rtf','RTF');
INSERT INTO "associations" VALUES('com.lab126.booklet.kpvbooklet','application','MT:application/rtf','true');
INSERT INTO "associations" VALUES('com.lab126.generic.extractor','extractor','GL:*.rtf','true');
INSERT INTO "mimetypes" VALUES('tcr','MT:application/tcr');
INSERT INTO "extenstions" VALUES('tcr','MT:application/tcr');
INSERT INTO "properties" VALUES('archive.displaytags.mimetypes','application/tcr','TCR');
INSERT INTO "associations" VALUES('com.lab126.booklet.kpvbooklet','application','MT:application/tcr','true');
INSERT INTO "associations" VALUES('com.lab126.generic.extractor','extractor','GL:*.tcr','true');
INSERT INTO "mimetypes" VALUES('txt','MT:text/plain');
INSERT INTO "extenstions" VALUES('txt','MT:text/plain');
INSERT INTO "properties" VALUES('archive.displaytags.mimetypes','text/plain','TXT');
INSERT INTO "associations" VALUES('com.lab126.booklet.kpvbooklet','application','MT:text/plain','true');
INSERT INTO "associations" VALUES('com.lab126.generic.extractor','extractor','GL:*.txt','true');
INSERT INTO "mimetypes" VALUES('xps','MT:application/xps');
INSERT INTO "extenstions" VALUES('xps','MT:application/xps');
INSERT INTO "properties" VALUES('archive.displaytags.mimetypes','application/xps','XPS');
INSERT INTO "associations" VALUES('com.lab126.booklet.kpvbooklet','application','MT:application/xps','true');
INSERT INTO "associations" VALUES('com.lab126.generic.extractor','extractor','GL:*.xps','true');
INSERT INTO "mimetypes" VALUES('zip','MT:application/zip');
INSERT INTO "extenstions" VALUES('zip','MT:application/zip');
INSERT INTO "properties" VALUES('archive.displaytags.mimetypes','application/zip','ZIP');
INSERT INTO "associations" VALUES('com.lab126.booklet.kpvbooklet','application','MT:application/zip','true');
INSERT INTO "associations" VALUES('com.lab126.generic.extractor','extractor','GL:*.zip','true');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment