src/WebServer.js
const Express = require("express");
const fs = require("fs");
const MIME = require("mime-types");
const Chalk = require("chalk");
const VM = require("vm");
const BodyParser = require("body-parser");
const Path = require("path");
const _busboy_ = require('busboy');
const AnalysisHelper = require("./AnalysisHelper.js");
const ConnectorFactory = require('./ConnectorFactory');
const Decompiler = require("./Decompiler.js");
const Configuration = require("./Configuration.js");
const Uploader = require('./Uploader');
const Downloader = require('./Downloader');
const UT = require("./Utils.js");
const Logger = require("./Logger.js")();
const CLASS = require("./CoreClass.js");
const UTF8 = require("./UTF8.js");
const ANDROID = require("./AndroidAppComponents.js");
const INTENT = require("./IntentFactory.js");
const Simplifier = require("./Simplifier.js");
const WebTemplateEngine = require('./WebTemplateEngine');
const Installer = require('./Installer').Installer;
const PlatformManager = require('./PlatformManager');
const DeviceManager = require('./DeviceManager');
const InspectorManager = require('./InspectorManager');
const FridaHelper = require('./FridaHelper');
const ApkHelper = require('./ApkHelper');
/**
* @namespace WebServer.MimeHelper
*/
const MimeHelper = {
/**
* To detect if the MIME type is a font
*
* @param {String} mime Mime type
* @returns {Boolean} TRUE if the MIME type is a font
* @function
*/
isFontFile: function (mime) {
let verdict = false;
["woff2", "woff", "ttf"].map(x => {
if (mime.indexOf(x) > -1) {
verdict = true;
}
});
return verdict;
}
}
/**
* Class representing Dexcalibur's web server
*
* @class
*/
class WebServer {
/**
*
* @param {Project} pProject
* @constructor
*/
constructor( pWebRoot) {
this.context = null;
this.project = null; //pProject;
this.tplengine = new WebTemplateEngine();
this.app = Express();
this.port = 8000;
// this.root = Path.join(this.project.config.dexcaliburPath, "webserver", "public");
// this.root = Path.join( __dirname, "webserver", "public");
this.root = pWebRoot;
console.log(this.root);
this.logs = {
access: []
};
this.uploader = null;
this.controller = null;
}
/**
* To set the active project
*
* @param {Project} pProject
* @method
*/
setProject( pProject){
this.project = pProject;
}
/**
* To set Dexcalibur engine
*
* @param {DexcaliburEngine} pEngine
* @method
*/
setContext( pContext){
this.context = pContext;
}
/**
* To get Express Application instance used by web server
*
* @returns {Express.Application} Instance of Express Application
* @method
*/
getApplication(){
return this.app;
}
/**
*
* @param {require('path')} pHome The path of the file containing home page
* @method
*/
newDispatcher( pHome){
let $ = this;
return function (req, res) {
//console.log(req.path);
let localPath = $.root + req.path, mime = null;
if (req.path.endsWith("/"))
localPath = Path.join($.root, pHome);
if (req.path.startsWith("/inspectors/")) {
//console.log(req.path.substr(1,req.path.length-1))
let inspector = req.path.substr(1, req.path.length - 1).split("/");
let relPath = "";
let iid = null;
if (inspector.length > 1) {
iid = inspector[1];
//console.log(inspector)
//relPath = inspector.pop();
for (let i = 2; i < inspector.length; i++)
relPath = Path.join(relPath, inspector[i]);
//console.log(relPath);
//localPath = inspector[1]+"/web/"+localPath
localPath = Path.join( __dirname, "..", "inspectors");
localPath = Path.join(localPath, iid, "web", relPath);
//console.log("[WebServer::inspectors] Path = "+localPath);
//localPath = $.project.getInspector(inspector).getPath();
mime = MIME.lookup(Path.basename(localPath));
} else {
// redirect to /pages/inspectors?error=404
localPath = $.root + "/pages/inspectors?error=404";
mime = MIME.lookup($.root + "/pages/inspectors.html");
}
} else
mime = MIME.lookup(localPath.split("/").pop());
if (localPath.endsWith("bootstrap.min.css.map")) {
res.status(404).send("An error occured :");//+err.message);
return;
}
fs.readFile(localPath, (err, data) => {
// set good http headers into the response
res.set('Access-Control-Allow-Origin', '*');
if (err != null) {
$.logs.access.push("[404]:" + mime + " " + req.path + " => " + localPath);
res.status(404).send("An error occured :" + err.message);
return;
}
if (MimeHelper.isFontFile(mime)) {
res.set('accept-ranges', "bytes");
res.set('vary', 'Accept-Encoding');
res.set('Content-Type', mime);
} else {
res.set('Content-Type', mime);
res.set('X-XSS-Protection', '0; mode=block');
res.set('X-Frame-Options', 'SAMEORIGIN');
res.set('X-Content-Type-Options', 'nosniff');
//res.set('Content-Security-Policy', 'nosniff');
}
$.logs.access.push("[200]:" + mime + " " + req.path + " => " + localPath);
// replace template tags
if (localPath.endsWith(".html"))
data = $.tplengine.process(data);
res.status(200).send(data);
});
}
}
/**
* To init routes to static content
*
* @method
*/
initStaticRoutes(){
// define middleware
this.app.use(BodyParser.urlencoded({ extended: false }));
this.app.use(BodyParser.json());
// start server
this.app.get('/', this.controller);
this.app.get('/pages/*', this.controller);
this.app.get('/dist/*', this.controller);
this.app.get('/data/*', this.controller);
this.app.get('/js/*', this.controller);
this.app.get('/less/*', this.controller);
this.app.get('/vendor/*', this.controller);
}
/**
* To init routes when Dexcalibur runs install mode
*
* @method
*/
initInstallRoutes(){
let $ = this;
this.controller = this.newDispatcher( Path.join("pages","install.html"));
// init routes serving static contents
this.initStaticRoutes();
this.app.use('/api/settings', require("./routes/InstallRoutes"));
}
/**
* To initialize routes of the web server
*
* @method
*/
initRoutes() {
let $ = this;
this.controller = this.newDispatcher("index.html");
// init routes serving static contents
this.initStaticRoutes();
this.app.get('/index.html', this.controller);
this.app.get('/inspectors/*', this.controller);
// Inspector frontController
this.app.route('/api/inspectors/:inspectorID')
.get(function (req, res) {
let insp = InspectorManager.getInstance().getEnabledInspector( $.project, req.params.inspectorID);
if (insp == false) {
res.status(404).send(JSON.stringify({ msg: "Inspector cannot be retrieved" }));
return false;
}
insp.performGet(req, res);
})
.post(function (req, res) {
let insp = InspectorManager.getInstance().getEnabledInspector( $.project, req.params.inspectorID);
if (insp === false) {
res.status(404).send(JSON.stringify({ msg: "Inspector cannot be retrieved" }));
return false;
}
insp.performPost(req, res);
})
// Connectors
this.app.route('/api/connectors')
.get(function (req, res) {
res.status(200).send(
JSON.stringify(
ConnectorFactory.getInstance().toJsonObject()
)
);
});
// API routes
this.app.route('/api/platform/list')
.get(function (req, res) {
// collect
let dev = {
platforms: PlatformManager.getInstance().getRemote()
};
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/platform/install')
.post(async function (req, res) {
let mgr, dev, platform;
dev = {
status: false
};
mgr = PlatformManager.getInstance();
platform = mgr.getRemotePlatform(req.body['uid']);
if(platform !== null){
dev.status = await mgr.install(platform);
}
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/workspace/upload')
.post(function (req,res){
$.uploader.newUpload( req, res, function( vId) {
res.status(200).send(JSON.stringify({ success:true, upload:vId }));
res.end();
});
});
this.app.route('/api/workspace/list')
.get(function (req, res) {
// collect
let dev = {
projects: $.context.getProjects()
};
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/workspace/new')
.post(async function (req, res) {
const PLATFORM_MODE = ['dev','min','max'];
let engine = $.context;
let project = null;
let dm = null;
let device = null;
let path = null;
let platform = null;
let success = false, connFact = null;
dm = DeviceManager.getInstance();
await dm.scan();
if(typeof req.body['name']!='string'){
res.status(200).send(JSON.stringify({ success:false, code:1, msg:"Invalid project name"}));
return;
}
if($.context.isValidProjectUID(req.body['name'])==false){
res.status(200).send(JSON.stringify({ success:false, code:1, msg:"Invalid project UID"}));
return;
}
if(req.body['dev'] != null){
device = dm.getDevice( req.body['dev']);
if(device == null || !device.isEnrolled()){
res.status(200).send(JSON.stringify({ success:false, code:2, msg:"Device unknow or not enrolled "}));
return;
}
}
try{
// first download remote application
// on error : ne‹ project will not create.
switch(req.body['type'])
{
case 'select':
if(device == null){
res.status(200).send(JSON.stringify({ success:false, code:2, msg:"Device unknow or not enrolled "}));
res.end();
}
platform = device.getPlatform();
path = device.pullTemp( req.body['path'] );
break;
case 'download':
path = await Downloader.downloadTemp(req.body['url'], { mode:0o666, encoding:'binary', force:true });
break;
case 'upload':
path = $.uploader.getPathOf(req.body['uploadid']);
break;
case 'fromfs':
path = req.body['path'];
break;
}
if(platform==null){
if(PLATFORM_MODE.indexOf(req.body['platform'])==-1){
platform = PlatformManager.getInstance().getPlatform( req.body['platform']);
if(platform==null){
res.status(200).send(JSON.stringify({ success:false, code:6, msg:"This platform is not installed."}));
return;
}
}else{
platform = req.body['platform'];
}
}
// check if file exists an it is not empty
if( (!fs.existsSync(path)) || (false)){
res.status(200).send(JSON.stringify({ success:false, code:5, msg:"APK file not found "}));
return;
}
// create project : UID , APK [, Device]
project = await $.context.newProject(req.body['name'], path, device);
// to set connector
connFact = ConnectorFactory.getInstance();
if((typeof req.body['connector']== "string") && connFact.isValidConnector(req.body['connector'])){
project.setConnector(req.body['connector']);
}else {
project.setConnector(connFact.getDefaultConnector());
}
if(project != null){
// sync project platform with target platform or APK
success = project.synchronizePlatform( platform);
}
if(success){
project = await project.fullscan();
success = project.isReady();
}
// collect
let dev = {
success: success // project.isReady()
};
res.status(200).send(JSON.stringify(dev));
}catch(err){
console.log(err);
$.setProject(null);
res.status(200).send(JSON.stringify({ success:false, code:-1, msg:"An error occured while project initializing"}));
}
return ;
});
this.app.route('/api/workspace/open')
.get(async function (req, res) {
// refresh connected device
await DeviceManager.getInstance().scan();
let project = null;
project = $.context.getProject( req.query.uid);
if(project != null){
if($.project == null){
$.setProject( project);
}
else if($.project != null && $.project.getUID()!==req.query.uid){
$.setProject( project);
}
res.status(200).send(JSON.stringify({
success: project.isReady()
}));
return ;
}
//$.project =
project = await $.context.openProject( req.query.uid );
// collect
let dev = {
success: project.isReady()
};
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/workspace/delete')
.post(async function (req, res) {
// collect
let dev = {
success: $.context.deleteProject( req.body['uid'] )
};
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/workspace/availability')
.get(function (req, res) {
let proj = null, availability = true, dev=null, code=-1;
switch( req.query.field)
{
case "project.uid":
if($.context.isValidProjectUID(req.query.value)==false){
availability = false;
code = 1;
break;
}
proj = $.context.workspace.listProjects();
proj.map((vProject)=>{
if(vProject == req.query.value)
availability = false;
code = 2;
})
break;
case "device":
dev = DeviceManager.getInstance().getDevice( req.query.value);
if(dev == null || !dev.isEnrolled()){
availability = false;
return;
}
break;
}
// collect
let data = {
availability: availability,
code: code
};
res.status(200).send(JSON.stringify(data));
});
this.app.route('/api/device/connect')
.post(async function(req, res){
let dm = DeviceManager.getInstance();
let ip = req.body['ip'];
let port = req.body['port'];
let device = null;
let data = false;
try{
if(req.body['dev'] !== null){
device = dm.getDevice(req.body['dev']);
if(device != null)
Logger.debug('[WEBSERVER][/api/device/connect] Device selected : ',device.getUID());
else
Logger.debug('[WEBSERVER][/api/device/connect] Device not found.');
}
if(ip=="" && port==""){
let b = device.getBridge('adb+tcp');
if( b!= null ){
data = await dm.connect( b.ip, b.port, device);
}
}else
data = await dm.connect(ip, port, device);
if(data){
dm.save();
}
data = { success: data };
if(data.success == false){
data.msg = 'An unknow error happened. See Dexcalibur logs/output for more details.';
res.status(500);
}else{
res.status(200);
}
}catch(err){
data = { success:false, msg:err.message };
res.status(500)
}
res.send(JSON.stringify(data));
});
this.app.route('/api/device/clear/:deviceid')
.post(function(req, res){
let dm = DeviceManager.getInstance();
let deviceid = req.params['deviceid'];
let dev;
try{
dev = { success: dm.clear(deviceid) };
res.status(200);
}catch(err){
dev = { success:false, msg:err.message };
res.status(500);
}
res.send(JSON.stringify(dev));
});
this.app.route('/api/device/clear')
.post(function(req, res){
let dm = DeviceManager.getInstance();
let dev;
try{
dev = { success: dm.clear( null) };
res.status(200);
}catch(err){
dev = { success:false, msg:err.message };
res.status(500);
}
console.log(dev);
res.send(JSON.stringify(dev));
});
this.app.route('/api/device/bridge/:name/kill')
.post(async function(req, res){
let dm = DeviceManager.getInstance();
let name = req.params['name'];
let dev;
try{
dev = dm.getBridgeFactory(name).newGenericWrapper();
dev = { success: await dev.kill() };
}catch(err){
console.log(err);
dev = { success:false, msg:err };
}
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/device/enroll')
.post(async function(req, res){
let dm = DeviceManager.getInstance();
let uid = req.body['uid'];
let opts = req.body['opts'];
let dev;
try{
dev = { success: await dm.enroll(uid, opts) };
}catch(err){
console.log(err);
dev = { success:false, msg:err };
}
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/device/enroll/status')
.get(function(req, res){
let dm = DeviceManager.getInstance();
let uid = req.body['uid'];
let status;
status = dm.getEnrollStatus(uid);
if(status == null){
res.status(200).send(JSON.stringify({
msg: null,
progress: null,
extra: null
}));
}else{
res.status(200).send(JSON.stringify({
msg: status.getMessage(),
progress: status.getProgress(),
extra: status.getExtra()
}));
}
});
this.app.route('/api/device')
.get(async function (req, res) {
// scan connected devices
let dm;
dm = DeviceManager.getInstance();
await dm.scan();
dm.save();
res.status(200).send(JSON.stringify({
devices: dm.toJsonObject( {}, {
device: {
profile: false,
frida: false,
bridge: {
path: false
}
}
})
}));
});
this.app.route('/api/device/applications')
.get(async function (req, res) {
// scan connected devices
let dev, dm, opts='-f';
dm = DeviceManager.getInstance();
dev = dm.getDevice( req.query.uid );
if(dev.isEnrolled() == false){
res.status(404).send(JSON.stringify({
msg: 'Device is not enrolled'
}));
return;
}
dm = dev.getDefaultBridge().listPackages(opts);
dev = [];
dm.map( (x)=>{
dev.push(x.toJsonObject())
});
res.status(200).send(JSON.stringify({
device: req.query.uid,
apps:dev
}));
});
this.app.route('/api/device/setDefault')
.post(function (req, res) {
// collect
let uid = req.body["uid"];
let dm = DeviceManager.getInstance();
let dev = null;
res.set('Content-Type', 'text/json');
if(uid != null){
dev = dm.getDevice(uid);
if(dev==null){
res.status(404).send(JSON.stringify({
error: "Invalid device ID",
errcode: "DM2"
}));
return 1;
}
if($.project != null){
$.project.setDevice(dm.getDevice(uid));
$.project.save();
}
// TODO : change > defaultDevice => project
dm.setDefault(uid);
res.status(200).send(JSON.stringify({
msg: "Device <b>"+uid+"</b> is the new default device."
}));
return 1;
}else{
res.status(404).send(JSON.stringify({
error: "Invalid device ID",
errcode: "DM1"
}));
return 1;
}
});
this.app.route('/api/device/:uid/bridge')
.put(async function (req, res) {
// scan connected devices
let dev=null, bridge=null, dm=null, result=false;
try{
dm = DeviceManager.getInstance();
dev = dm.getDevice(req.params.uid);
bridge = dev.getBridge(req.body['name']);
if(bridge.up==false){
if(bridge.isNetworkTransport()){
result = await dm.connect( bridge.ip, bridge.port, dev);
if(result){
dev.setDefaultBridge(req.body['name']);
dm.save();
res.status(200).send({ success: true });
return ;
}else{
res.status(500).send({ success: false, msg:'Connection over TCP failed.' });
return ;
}
}else{
res.status(500).send({ success: false, msg:'Please connect the device through USB and retry.' });
return ;
}
}else{
dev.setDefaultBridge(req.body['name']);
dm.save();
res.status(200).send({ success: true });
return;
}
}catch(err){
res.status(500).send({ success: false, msg:err.message });
}
});
// todo : replace by device manager scan()
this.app.route('/api/packageList')
.get(function (req, res) {
// scan connected devices
$.project.packagePatcher.scan();
// collect
let packages = {
data: $.project.packagePatcher.toJsonObject()
};
res.status(200).send(JSON.stringify(packages));
});
// todo : replace by splash/select app
this.app.route('/api/changeWorkspace/:projectIdentifier')
.get(function (req, res) {
// scan connected devices
$.project.changeProject(req.params.projectIdentifier);
// collect
res.status(200).send("{\"status\": \"ok\"}");
});
// todo : replace by splash/select app
this.app.route('/api/pullProject/:packageIdentifier')
.get(function (req, res) {
// scan connected devices
//console.log(req.params.packageIdentifier)
$.project.packagePatcher.pullPackage(req.params.packageIdentifier);
// collect
$.project.changeProject(req.params.packageIdentifier);
res.status(200).send(JSON.stringify({ message: "ok" }));
});
// not used
this.app.route('/api/stats')
.get(function (req, res) {
// collect
let dev = {
class: {
count: $.project.find.class().count()
},
method: {
count: $.project.find.method().count()
},
field: {
count: $.project.find.field().count()
},
calls: {
count: $.project.find.calls().count()
},
activity: {
count: $.project.find.nocase().class("name:activity$").count()
},
provider: {
count: $.project.find.nocase().class("name:provider$").count()
},
service: {
count: $.project.find.nocase().class("name:service$").count()
},
broadcast: {
count: $.project.find.nocase().class("name:broadcast$").count()
},
nfc_ctrl: {
count: $.project.find.nocase().class("name:nfccontroller").count()
},
mst_ctrl: {
count: $.project.find.nocase().class("name:mstcontroller").count()
}
};
res.status(200).send(JSON.stringify(dev));
});
/*
this.app.route('/api/probe')
.get(function(req,res){
// collect
let dev = {
data: $.project.find.class().toJsonObject()
};
res.status(200).send(JSON.stringify(dev));
});
*/
this.app.route('/api/class')
.get(function (req, res) {
// collect
let dev = {
data: $.project.find.class().toJsonObject()
};
for (let i in dev.data) {
for (let k in dev.data[i].methods) {
if ($.project.hook.isProbing(dev.data[i].methods[k])) {
dev.data[i].methods[k].probing = true;
} else {
dev.data[i].methods[k].probing = false;
}
}
}
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/probe')
.get(function (req, res) {
let hooks = $.project.hook.list();
let data = { data: [] };
for (let i in hooks) {
data.data.push(hooks[i].toJsonObject());
}
res.status(200).send(JSON.stringify(data));
});
this.app.route('/api/inspector')
.get(function (req, res) {
let insp = InspectorManager.getInstance().getInspectorsOf($.project);
let data = { data: [] };
for (let i in insp) {
data.data.push(insp[i].toJsonObject());
}
res.status(200).send(JSON.stringify(data));
});
this.app.route('/api/probe/server/start')
.post(async function (req, res) {
let device = null;
if(req.body['dev']){
device = DeviceManager.getInstance().getDevice(req.param.dev);
if(device==null){
res.status(200).send(JSON.stringify({
success: false,
code: 1,
msg: "Selected device cannot be found."
}));
return ;
}
}else{
device = $.project.getDevice();
if(device==null) {
res.status(200).send(JSON.stringify({
success: false,
code: 2,
msg: "There is not default device associated to this project. Visit 'Settings > Target device'."
}));
return ;
}
}
if(device.isConnected()==false){
res.status(200).send(JSON.stringify({
success: false,
code: 3,
msg: "Device is offline."
}));
return ;
}
let status = null;
try{
status = await FridaHelper.startServer( device, {
path: req.body['path'],
privileged: (req.body['privileged']=="true"? true: false)
});
res.status(200).send(JSON.stringify({
success: status
}));
}catch(err){
console.log(err);
res.status(200).send(JSON.stringify({
success: false
}));
}
});
this.app.route('/api/probe/server/status')
.get(async function (req, res) {
let device = null;
if(req.param.dev){
device = DeviceManager.getInstance().getDevice(req.param.dev);
}else{
device = $.project.getDevice();
}
try{
res.status(200).send(JSON.stringify({
success: await FridaHelper.getServerStatus( device)
}));
}catch(err){
console.log(err);
res.status(200).send(JSON.stringify({
success: false,
code: -1
}));
}
});
this.app.route('/api/probe/start')
.post(function (req, res) {
try{
switch(req.body.type){
case "spawn-self":
Logger.info(`[WEBSERVER] Start hooking [app=${$.project.getPackageName()}, type=spawn-self]`);
$.project.hook.startBySpawn($.project.getPackageName());
res.status(200).send(JSON.stringify({ enable: true }));
break;
case "spawn":
Logger.info(`[WEBSERVER] Start hooking [app=${req.body.app}, type=spawn]`);
$.project.hook.startBySpawn(req.body.app);
res.status(200).send(JSON.stringify({ enable: true }));
break;
case "attach-gadget":
Logger.info(`[WEBSERVER] Start hooking [pid=Gadget, type=attach-gadget]`);
$.project.hook.startByAttachToGadget();
res.status(200).send(JSON.stringify({ enable: true }));
break;
case "attach-app-self":
Logger.info(`[WEBSERVER] Start hooking [app=${req.body.app}, type=attach-app-self]`);
$.project.hook.startByAttachToApp($.project.getPackageName());
res.status(200).send(JSON.stringify({ enable: true }));
break;
case "attach-app":
Logger.info(`[WEBSERVER] Start hooking [app=${req.body.app}, type=attach-app-x]`);
$.project.hook.startByAttachToApp(req.body.app);
res.status(200).send(JSON.stringify({ enable: true }));
break;
case "attach-pid":
Logger.info(`[WEBSERVER] Start hooking [pid=${req.body.pid}, type=attach-to-pid`);
$.project.hook.startByAttachTo(req.body.pid);
res.status(200).send(JSON.stringify({ enable: true }));
break;
default:
res.status(404).send(JSON.stringify({ err: 'Invalid start type' }));
break;
}
}catch(exception){
console.log(exception);
res.status(404).send(JSON.stringify({ err: exception }));
}
});
/**
* To download the resulting Frida script
*/
this.app.route('/api/probe/download')
.get(function (req, res) {
let script = $.project.hook.prepareHookScript();
res.set('Content-Type', 'application/octet-stream');
res.set('Content-Length', script.length);
res.set('Content-Disposition', 'attachment; filename="hook.js"');
res.set('Expires', '0');
res.status(200).send(script);
});
this.app.route('/api/probe/msg')
.get(function (req, res) {
if($.project == null){
res.status(404).send(JSON.stringify({}));
return null;
}
let sess = $.project.hook.lastSession();
if (sess == null) {
res.status(404).send({ msg: "No session" });
return;
}
let startAt = req.query.startAt;
let size = req.query.size;
if (!sess.hasMessages(startAt, size)) {
res.status(404).send({ msg: "No messages" });
return;
}
let data = { data: sess.toJsonObject(parseInt(startAt,10), parseInt(size,10)) };
res.status(200).send(JSON.stringify(data));
});
this.app.route('/api/probe/:method')
.post(function (req, res) {
let meth = $.project.find.get.method(UT.decodeURI(UT.b64_decode(req.params.method)));
if (meth == null) {
console.log(UT.decodeURI(UT.b64_decode(req.params.method)));
res.status(404).send(JSON.stringify({ msg: "Method not found" }));
return;
}
if (meth.name == "<clinit>") {
res.status(404).send(JSON.stringify({ msg: "Static blocks (<clinit>) cannot be hooked" }));
return;
}
let probe = $.project.hook.getProbe(meth);
if (probe == null) {
probe = $.project.hook.probe(meth);
}
// collect
let dev = {
enable: probe.isEnable()
};
//if(hook.enable)
$.project.trigger({
type: "probe.new",
data: {
hook: probe,
method: meth
}
});
res.status(200).send(JSON.stringify(dev));
/*else
res.status(403).send(JSON.stringify(dev));*/
})
.put(function (req, res) {
let meth = $.project.find.get.method(UT.decodeURI(UT.b64_decode(req.params.method)));
if (meth == null) {
res.status(404).send({ error: "No probe ID given" });
return;
}
let status = req.query.enable;
Logger.info(status, req.query);
if (status === undefined) {
res.status(404).send({ error: "No search request" });
return;
}
let hook = $.project.hook.getProbe(meth);
if (status == "true")
hook.setEnable(true);
else
hook.setEnable(false);
// collect
let dev = {
enable: hook.isEnable()
};
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/hook/app/detach')
.post(async function (req, res) {
Logger.info("[REST] /api/hook/app/detach POST");
// get hook instance by ID
let session = $.project.hook.lastSession();
if (session.fridaScript == null) {
res.status(200).send({ success: false, error: "Invalid frida script" });
}else{
let a = await session.fridaScript.unload();
res.status(200).send(JSON.stringify({ success: await session.fridaScript.unload(a) }));
}
})
this.app.route('/api/hook/app/kill')
.post(async function (req, res) {
Logger.info("[REST] /api/hook/frida/kill POST");
// get hook instance by ID
let session = $.project.hook.lastSession();
if(session == null){
res.status(200).send({ success: false, error: "Unknow PID" });
return;
}
if (session.pid == null) {
res.status(200).send({ success: false, error: "Invalid PID" });
}else{
let o = await $.project.getDevice().privilegedExecSync('kill '+session.pid, {detached:false});
res.status(200).send(JSON.stringify({ success: true }));
}
})
this.app.route('/api/hook/frida/exec')
.post(async function(req, res){
let newCode = req.body['code[]'].join("\n");
let output = null;
try{
switch(req.body.type){
case "spawn-self":
Logger.info(`[WEBSERVER] Start with frida console [app=${$.project.getPackageName()}, type=spawn-self]`);
output = await FridaHelper.exec(newCode, FridaHelper.SPAWN, $.project.getPackageName());
break;
case "spawn":
Logger.info(`[WEBSERVER] Start with frida console [app=${req.body.app}, type=spawn]`);
output = await FridaHelper.exec(newCode, FridaHelper.SPAWN, req.body.app);
break;
case "attach-gadget":
Logger.info(`[WEBSERVER] Start with frida console [pid=Gadget, type=attach-gadget]`);
output = await FridaHelper.exec(newCode, FridaHelper.ATTACH_BY_NAME, "Gadget");
break;
case "attach-app-self":
Logger.info(`[WEBSERVER] Start with frida console [app=${req.body.app}, type=attach-app-self]`);
output = await FridaHelper.exec(newCode, FridaHelper.ATTACH_BY_NAME, $.project.getPackageName());
break;
case "attach-app":
Logger.info(`[WEBSERVER] Start with frida console [app=${req.body.app}, type=attach-app-x]`);
output = await FridaHelper.exec(newCode, FridaHelper.ATTACH_BY_NAME, req.body.app);
break;
case "attach-pid":
Logger.info(`[WEBSERVER] Start with frida console [pid=${req.body.pid}, type=attach-to-pid`);
output = await FridaHelper.exec(newCode, FridaHelper.ATTACH_BY_PID, req.body.pid);
break;
default:
res.status(404).send(JSON.stringify({ err: 'Invalid start type' }));
return;
break;
}
res.status(200).send(JSON.stringify({ output: await output }));
}catch(exception){
console.log(exception);
res.status(404).send(JSON.stringify({ err: exception }));
}
});
this.app.route('/api/hook/:hookid')
.get(function (req, res) {
Logger.info("[REST] /api/hook/:hookid GET");
// get hook instance by ID
let hook = $.project.hook.getHookByID(
req.params.hookid
);
if (hook == null) {
res.status(404).send({ success: false, error: "Invalid hook ID given" });
}else{
res.status(200).send(JSON.stringify({ success: true, hook: hook.toJsonObject() }));
}
})
.put(function (req, res) {
Logger.info("[REST] /api/hook/:hookid EDIT");
let hook = $.project.hook.getHookByID(
req.params.hookid
);
if (hook == null) {
res.status(404).send({ success: false, error: "Invalid hook ID given" });
return;
}
let newCode = req.body['code[]'].join("\n");
//hook.script = newCode;
hook.modifyScript(newCode);
//let success = $.project.hook.removeHook(hook);
res.status(200).send(JSON.stringify({ success: true }));
})
.delete(function (req, res) {
let hook = $.project.hook.getHookByID(
//UT.b64_decode(req.params.hookid)
req.params.hookid
);
Logger.info("[REST] /api/hook/:hookid REMOVE");
if (hook == null) {
res.status(404).send({ error: "No probe ID given" });
return;
}
let success = $.project.hook.removeHook(hook);
res.status(200).send(JSON.stringify({ success: (success != null) ? true : false }));
});
this.app.route('/api/hook/enable/:hookid')
.put(function(req, res){
let dev={}, hook=null;
if(req.params.hookid=="all"){
hook = $.project.hook.list();
for(let i in hook){
hook[i].enable();
dev[i] = {enable: hook[i].isEnable() };
}
}else{
hook = $.project.hook.getHookByID(
req.params.hookid
);
hook.enable();
// collect
dev = {
enable: hook.isEnable()
};
}
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/hook/disable/:hookid')
.put(function(req, res){
let dev={}, hook=null;
if(req.params.hookid=="all"){
hook = $.project.hook.list();
for(let i in hook){
hook[i].disable();
dev[i] = {enable: hook[i].isEnable() };
}
}else{
hook = $.project.hook.getHookByID(
req.params.hookid
);
hook.disable();
// collect
dev = {
enable: hook.isEnable()
};
}
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/class/:id')
.put(function (req, res) {
// collect
let obj = $.project.find.get.class(UT.decodeURI(UT.b64_decode(req.params.id)));
if (obj == null) {
res.status(404).send(JSON.stringify({ error: "Class not found" }));
return;
}
let alias = req.body['alias'];
//console.log(alias);
if(alias != null){
obj.setAlias(alias);
$.project.trigger({
type: "class.alias.update",
cls: obj
});
}
res.status(200).send(JSON.stringify({ success: true }));
});
this.app.route('/api/class/implements/:id')
.get(function (req, res) {
// collect
let dev = {};
let cls = $.project.find.get.class(UT.decodeURI(UT.b64_decode(req.params.id)));
// let clss = $.project.find.classImplementing(cls);
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/graph/:graph_type/:type/:id')
.get(function (req, res) {
let data = {}, ret = null, from = null;
let graphType = {
cgfrom: "callgraph_from",
cgto: "callgraph_to"
};
let gtype = null;
let depth = (req.query.depth != null) ? parseInt(req.query.depth, 10) : null;
for (let k in graphType)
if (req.params.graph_type === k) {
gtype = graphType[k];
break;
}
if (gtype === null) {
res.status(404).send(JSON.stringify({ status: 404, msg: { err: "Graph type not found" } }))
return;
}
switch (req.params.type) {
case 'method':
// retrieve the method
from = $.project.find.get.method(UT.decodeURI(UT.b64_decode(req.params.id)));
if (from == null) {
ret = { status: 404, msg: { err: "Given method not found" } };
break;
}
// compute graph data
if (depth !== null)
ret = { status: 200, msg: { data: $.project.graph[gtype](from, 1, depth) } };
else
ret = { status: 200, msg: { data: $.project.graph[gtype](from, 1) } };
break;
default:
ret = { status: 404, msg: { err: "Unknow Type" } };
break;
}
res.status(ret.status).send(JSON.stringify(ret.msg))
})
/**
* To get full information about a method
*/
this.app.route('/api/method/decompile/:id')
.get(function (req, res) {
// collect
let dev = {};
let method = $.project.find.get.method(UT.decodeURI(UT.b64_decode(req.params.id)));
let decompiler = Decompiler.getInstance($);
let simplifyLvl = (req.query.level!=undefined)? req.query.level : 0;
dev = decompiler.decompile(method, simplifyLvl);
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/method/simplify/:id')
.post(function (req, res) {
// collect
let dev = {};
let method = $.project.find.get.method(UT.decodeURI(UT.b64_decode(req.params.id)));
let simplifier = Simplifier.getInstance($.project);
// init body
simplifier.setParametersValues(req.body.params);
simplifier.setInitParentClass(req.body.clinit);
simplifier.setMaxDepth(req.body.depth);
let simplifyLvl = (req.body.level!=undefined)? req.body.level : 0;
dev = simplifier.simplify(method, simplifyLvl);
res.status(200).send(JSON.stringify(dev));
});
/**
* To get full information about a method
*/
this.app.route('/api/method/disass/:id')
.get(function (req, res) {
// collect
let dev = {};
let method = $.project.find.get.method(UT.decodeURI(UT.b64_decode(req.params.id)));
dev.disass = method.disass({ raw: true });
res.status(200).send(JSON.stringify(dev));
});
/**
* To enumerate exisiting categories and tags
*/
this.app.route('/api/tags')
.get(function (req, res) {
// collect
let dev = {
data: []
};
let tagc = $.project.analyze.getTagCategories();
for (let i = 0; i < tagc.length; i++) {
dev.data.push(tagc[i].toJsonObject());
}
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/method')
.get(function (req, res) {
// collect
let dev = {
data: $.project.find.method().toJsonObject()
};
res.status(200).send(JSON.stringify(dev));
});
/**
* To get xref of a given method by its ID
*/
this.app.route('/api/method/xref/:id')
.get(function (req, res) {
let type = req.query.type;
// collect
let method = $.project.find.get.method(UT.decodeURI(UT.b64_decode(req.params.id)));
if (method == null) {
Logger.error("XRef to > Given method not found :",UT.decodeURI(UT.b64_decode(req.params.id)));
res.status(404).send(JSON.stringify({ err: "method not found" }))
}
let data = [], tmp = null, refs = null, r2 = null;
switch (type) {
case "from":
Object.keys(method.getMethodUsed()).forEach(function (x) {
let m = $.project.find.get.method(x);
tmp = {
// method signature
s: m.signature(),
// aliased signature
a: m.__aliasedCallSignature__,
// return type signature
r: (m.getReturnType() != null ? m.getReturnType().signature() : null),
// tags
tags: m.getTags()
};
// args signatures
tmp.p = [];
if (m.hasArgs())
m.getArgsType().map(x => tmp.p.push(x.signature()));
data.push(tmp);
});
/*
Object.keys(method.getClassUsed()).forEach( x => data.push({
// method signature
s: x,
// type
t: "c"
}));*/
Object.keys(method.getFieldUsed()).forEach(x => data.push({
// method signature
s: x,
// type
t: "f"
}));
res.status(200).send(JSON.stringify({ data: data }));
break;
case "to":
refs = method.getCallers();
//console.log(refs);
for (let i = 0; i < refs.length; i++) {
//r2 = $.project.find.get.method(refs[i]);
r2 = refs[i];
if( (r2 instanceof CLASS.Method) == false){
r2 = $.project.find.get.method(r2)
}
tmp = {
// method signature
s: r2.signature(),
// aliased signature
a: r2.__aliasedCallSignature__,
// return type signature
r: (r2.getReturnType() != null ? r2.getReturnType().signature() : null),
// tags
tags: r2.getTags()
};
// args signatures
tmp.p = [];
if (r2.hasArgs())
r2.getArgsType().map(x => tmp.p.push(x.signature()));
data.push(tmp);
}
res.status(200).send(JSON.stringify({ data: data }));
break;
default:
res.status(500).send(JSON.stringify({ err: "type invalid" }));
break;
}
});
this.app.route('/api/method/:id')
.get(function (req, res) {
// collect
let dev = {};
let callers = [];
//console.log(UT.decodeURI(UT.b64_decode(req.params.id)));
let methRef = UT.decodeURI(UT.b64_decode(req.params.id));
let method = $.project.find.get.method(methRef);
if (method != null) {
dev = method.toJsonObject();
dev.disass = method.disass({ raw: true });
} else {
method = $.project.analyze.resolveMethod(methRef);
if (method != null) {
dev = method.toJsonObject();
dev.disass = method.disass({ raw: true });
} else {
console.log("Error : unable to find " + methRef);
res.status(404).send(JSON.stringify(dev));
return null;
}
}
dev._callers = callers;
res.status(200).send(JSON.stringify(dev));
})
.put(function (req, res) {
// collect
let method = $.project.find.get.method(UT.decodeURI(UT.b64_decode(req.params.id)));
if (method == null) {
res.status(404).send(JSON.stringify({ error: "Method not found" }));
return;
}
let alias = req.body['alias'];
if(alias != null){
method.setAlias(alias);
$.project.trigger({
type: "method.alias.update",
meth: method
});
}
res.status(200).send(JSON.stringify({ success: true }));
})
this.app.route('/api/package')
.get(function (req, res) {
// collect
let dev = {
data: $.project.find.package().toJsonObject(["name"])
};
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/manifest/content')
.get(function (req, res) {
// collect
let dev = {
data: $.project.getAppAnalyzer().dumpManifest()
};
res.status(200).send(JSON.stringify(dev));
})
.put(function (req, res) {
// collect
let newCode = req.body['code[]'].join("\n");
//hook.script = newCode;
$.project.getAppAnalyzer().updateManifest(newCode);
let dev = {};
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/manifest/activities')
.get(function (req, res) {
// collect
let dev = {
data: $.project.find.activity().toJsonObject()
};
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/manifest/activity/:id')
.get(function (req, res) {
let name = UT.decodeURI(UT.b64_decode(req.params.id));
let act = $.project.find.get.activity(name);
// collect
let dev = {
data: act.toJsonObject()
};
res.status(200).send(JSON.stringify(dev));
});
// receivers
this.app.route('/api/manifest/receivers')
.get(function (req, res) {
// collect
let dev = {
data: $.project.find.receiver().toJsonObject()
};
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/manifest/receiver/:id')
.get(function (req, res) {
let name = UT.decodeURI(UT.b64_decode(req.params.id));
let act = $.project.find.get.receiver(name);
let dev = null;
if (act instanceof ANDROID.Receiver) {
dev = {
data: act.toJsonObject()
};
res.status(200);
} else {
dev = {
err: "Receiver not found for the given ID",
errCode: null
}
res.status(404);
}
res.send(JSON.stringify(dev));
});
this.app.route('/api/project/:uid/app/info')
.get(function(req, res){
if(req.params.uid != "self"){
// not supported
res.status(404).send(JSON.stringify({ msg: 'Operation not supported (TODO)' }));
}else{
$.project.getApplication();
res.status(404).send(JSON.stringify({ msg: 'Operation not supported (TODO)' }));
}
});
// to get defaukt device of active project
this.app.route('/api/project/device')
.get(function(req, res){
if($.project == null){
res.status(500).send({ success:false, msg:'No active project' });
return ;
}
let dev = null;
try{
dev = $.project.getDevice();
if(dev!=null){
res.status(200).send({ success:true, msg:dev.toJsonObject({}, {
bridge: {
path: false
}
})
});
}else{
res.status(200).send({ success:true, msg:null });
}
}catch(excpt){
res.status(500).send({ success:false, msg:excpt.message });
}
return;
})
.post(function(req, res){
if($.project == null){
res.status(500).send({ success:false, msg:'No active project' });
return ;
}
let dev = null;
let uid = null;
try{
uid = req.body['device'];
dev = DeviceManager.getInstance().getDevice(uid);
if(dev != null){
$.project.setDevice(dev);
$.project.save();
}
res.status(200).send({ success:true });
}catch(excpt){
res.status(500).send({ success:false, msg:excpt.message });
}
return;
});
/*
this.app.route('/api/projection')
.get(function (req, res) {
// 'cmpType' should be a valid index into the database
// 'cmpID' should be a valid ID into 'cmpType' index
// 'cmpProjType' the type of projection to apply
let cmpType = req.params.cmp;
let cmpID = req.params.id;
let cmpProjType = req.params.proj;
console.log(req.params);
if(cmpID==null || cmpProjType==null || cmpType==null){
res.status(404);
res.send(JSON.stringify({ err: "Invalid params" }));
return;
}
if($.project.find.get[cmpType]==null){
res.status(404);
res.send(JSON.stringify({ err: "Invalid component type." }));
return;
}
let name = UT.decodeURI(UT.b64_decode(req.params.id));
let act = $.project.find.get[cmpType](name);
let dev = null;
let proj = $.project.analyze.getProjection(cmpProjType);
proj.process(act);
if (act instanceof ANDROID.Receiver) {
dev = {
data: act.toJsonObject()
};
res.status(200);
} else {
dev = {
err: "Receiver not found for the given ID",
errCode: null
}
res.status(404);
}
res.send(JSON.stringify(dev));
});
*/
this.app.route('/api/manifest/providers')
.get(function (req, res) {
// collect
let dev = {
data: $.project.find.provider().toJsonObject()
};
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/manifest/provider/:id')
.get(function (req, res) {
let name = UT.decodeURI(UT.b64_decode(req.params.id));
let act = $.project.find.get.provider(name);
// collect
let dev = {
data: act.toJsonObject()
};
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/manifest/services')
.get(function (req, res) {
// collect
let dev = {
data: $.project.find.service().toJsonObject()
};
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/manifest/service/:id')
.get(function (req, res) {
let name = UT.decodeURI(UT.b64_decode(req.params.id));
let act = $.project.find.get.service(name);
// collect
let dev = {
data: act.toJsonObject()
};
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/manifest/permissions')
.get(function (req, res) {
// collect
let dev = {
data: $.project.find.permission().toJsonObject()
};
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/manifest/permission/:id')
.get(function (req, res) {
let id = UT.b64_decode(req.params.id);
// collect
let dev = {
data: $.project.find.permission("name:" + UT.RegExpEscape(id)).toJsonObject()
};
res.set('Content-Type', 'text/json');
res.status(200).send(JSON.stringify(dev.data[0]));
});
this.app.route('/api/field/:id')
.get(function (req, res) {
// collect
let dev = {};
let sign = UT.decodeURI(UT.b64_decode(req.params.id));
//console.log(sign);
let field = $.project.find.get.field(sign);
dev = field.toJsonObject();
//dev.sets = $.project.find.settersOf(sign);
//dev.gets = $.project.find.gettersOf(sign);
// dev.htg = $.project.graph.htg(method);
//console.log(dev);
res.status(200).send(JSON.stringify(dev));
})
.put(function (req, res) {
// collect
let obj = $.project.find.get.field(UT.decodeURI(UT.b64_decode(req.params.id)));
if (obj == null) {
res.status(404).send(JSON.stringify({ error: "Field not found" }));
return;
}
let alias = req.body['alias'];
if(alias != null){
obj.setAlias(alias);
$.project.trigger({
type: "field.alias.update",
field: obj
});
}
res.status(200).send(JSON.stringify({ success: true }));
});
this.app.route('/api/field/xref/:id')
.get(function (req, res) {
// collect
let dev = { data: [] };
let field = $.project.find.get.field(UT.decodeURI(UT.b64_decode(req.params.id)));
if (field == null) res.status(404).send(JSON.stringify(dev));
Object.values(field.getSetters()).forEach(function (x) {
dev.data.push({
s: x.signature(),
a: x.getAlias(),
t: 's',
tags: x.getTags()
});
});
Object.values(field.getGetters()).forEach(function (x) {
dev.data.push({
s: x.signature(),
a: x.getAlias(),
t: 'g',
tags: x.getTags()
});
});
res.status(200).send(JSON.stringify(dev));
});
/*this.app.route('/api/field/:id/setters')
.get(function(req,res){
// collect
let dev = {};
//let sign = UT.decodeURI(UT.b64_decode(req.params.id));
let field = $.project.find.get.field(UT.decodeURI(UT.b64_decode(req.params.id)));
setters = field.getSetters();
getters = field.getGetters();
for(let i=0; i<setters.length; i++)
dev.setters = setters[i].toJsonObject();
for(let i=0; i<getters.length; i++)
dev.getters = getters[i].toJsonObject();
//dev = field.toJsonObject(["__setters"]);
// dev.htg = $.project.graph.htg(method);
res.status(200).send(JSON.stringify(dev));
});*/
this.app.route('/api/scanner')
.get(function (req, res) {
let o = [];
$.project.hook.refreshScanner();
for (let i in $.project.hook.scanners) {
o.push($.project.hook.scanners[i].toJsonObject());
}
res.status(200).send(JSON.stringify({ data: o }));
})
.post(function (req, res) {
/*let dev = {
data: $.dbm.getScannerDB().toJsonList()
};*/
res.status(404).send({ error: "Service unavailable" });
});
this.app.route('/api/scanner/run')
.get(function (req, res) {
let scannerId = req.query.id
if (scannerId == null) {
res.status(404).send(JSON.stringify({ data: null, err: "Invalid Hookset ID" }));
return;
}
let hookset = $.project.hook.getHookSet(UT.decodeURI(UT.b64_decode(scannerId)));
if (hookset == null) {
res.status(404).send(JSON.stringify({ data: null, err: "Hookset not found" }));
return;
}
hookset.deploy();
hook.run();
res.status(200).send(JSON.stringify({ data: { running: true }, err: null }));
});
this.app.route('/api/scanner/load')
.get(function (req, res) {
let scannerId = req.query.id
if (scannerId == null) {
res.status(404).send(JSON.stringify({ data: null, err: "Invalid Hookset ID" }));
return;
}
let hookset = $.project.hook.getHookSet(UT.decodeURI(UT.b64_decode(scannerId)));
if (hookset == null) {
res.status(404).send(JSON.stringify({ data: null, err: "Hookset not found" }));
return;
}
hookset.deploy();
res.status(200).send(JSON.stringify({ data: { running: true }, err: null }));
})
this.app.route('/api/finder')
.get(function (req, res) {
let search = req.query.search;
if (search == null) {
res.status(404).send({ error: "No search request" });
}
// decode the query
let u = UT.decodeURI(search);
Logger.info("[FINDER]: ", u);
let u1 = UT.b64_decode(u);
Logger.info("[FINDER]: ", u1);
let u2 = UT.decodeURI(u1);
Logger.info("[FINDER]: ", '$.project.find.' + u2 + ';');
//search = UT.decodeURI(UT.b64_decode(search));
Logger.info("[REST] /api/finder : ", u2);
//Logger.info("[REST] /api/finder : ",search);
// perform the requests (TODO: ajouter les erreur dans FinderResult)
let results = VM.runInNewContext('$.project.find.' + u2 + ';', { $: $ });
// collect
let dev = {
data: results.toJsonObject()
};
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/settings')
.get(function (req, res) {
// collect
let dev = {
cfg:null,
frida: null
};
let cfg = $.project.getConfiguration();
dev.cfg = cfg.toJsonObject();
dev.frida = cfg.getLocalFridaVersion();
res.status(200).send(JSON.stringify(dev));
})
.post(function (req, res) {
// collect
let data = req.body;
//console.log(data);
let dev = { status:null, invalid:null, err:null };
//let cfg = Configuration.from(data);
let cfg = $.project.getConfiguration();
// clone existing config
cfg = cfg.clone();
// import received data
cfg.import( data,
false, // autocomplete OFF
true // override ON
);
// verifiy fields
dev.invalid = cfg.verify();
try{
if(dev.invalid.length === 0){
Logger.success("Save configuration changes ...")
// Ask to current configuration to backup new configuration
$.project.getConfiguration().save(cfg);
}else{
Logger.error(dev.invalid);
}
}catch(err){
dev.err = err;
console.log(err);
}
/*
let dev = false;
let cfg = $.project.getConfiguration();
cfg = cfg.clone();
// not autocomplete, force overwrite
cfg.import( data,
false, // autocomplete
true // override
)
*/
res.status(200).send(JSON.stringify(dev));
});
this.app.route('/api/util/mkdir')
.post(function (req, res) {
// collect
let dev = { created:null, err:null };
let data = req.body;
console.log(data);
try{
if(fs.existsSync(data.path)==false){
fs.mkdirSync(data.path)
dev.created = fs.existsSync(data.path);
}else{
console.log("path exists");
}
}catch(err){
console.log(err);
dev.err = err;
}
res.status(200).send(JSON.stringify(dev));
})
/*
* Send an intent to to the default device
*/
this.app.route('/api/intent/send')
.post(async function (req, res) {
// collect
let uid = req.body["uid"];
let typeIntent = req.body["type"];
let extraAdb = req.body["extraAdb"];
let factory = null;
let app;
// if(req.body["custom"] == 1) custom=true;
// get intent filter
/*let intentFilter = $.project.getAppAnalyzer().getIntentFilter(req.body["type"],req.body["name"],uid);
if(intentFilter == null){
Logger.error("[WEBSERVER] IntentFilter not found for the given UID");
res.set('Content-Type', 'text/json');
res.status(404).send(JSON.stringify({ data: null, err: { err:"IntentFilter not found for the given UID", stderr: null} }));
return null;
}*/
// get default device
// resfresh dev
await DeviceManager.getInstance().scan();
let device = $.project.getDevice(); // devices.getDefault();
if(device == null || !device.isConnected()){
Logger.error("[WEBSERVER] Device not connected");
res.set('Content-Type', 'text/json');
res.status(404).send(JSON.stringify({ data: null, err: { err:"Device not connected" , stderr: null} }));
return null;
}
// init cb
/*let callbacks = {
stderr: function(err){
Logger.error("[WEBSERVER] An error occured (stderr): "+err);
res.set('Content-Type', 'text/json');
res.status(404).send(JSON.stringify({ data: null, err:{ err:"An error occured", stderr: err} }));
return ;
},
error: function(err){
Logger.error("[WEBSERVER] An error occured (err): "+err);
res.set('Content-Type', 'text/json');
res.status(404).send(JSON.stringify({ data: null, err:{ err:"An error occured", stderr: err} }));
return ;
},
stdout: function(out){
Logger.info("[WEBSERVER] Intent sent");
res.set('Content-Type', 'text/json');
res.status(200).send(JSON.stringify({ data: out, err:null }));
return ;
}
}*/
let callbacks = function(err, stdout, stderr){
if(err){
Logger.error("[WEBSERVER] An error occured (err): "+err);
res.set('Content-Type', 'text/json');
res.status(404).send(JSON.stringify({ data: null, err:{ err:"An error occured", stderr: err, msg:''+err} }));
return ;
}
if(stderr){
Logger.error("[WEBSERVER] An error occured (stderr): "+stderr);
res.set('Content-Type', 'text/json');
res.status(404).send(JSON.stringify({ data: null, err:{ err:"An error occured", stderr: stderr} }));
return ;
}else{
Logger.info("[WEBSERVER] Intent sent");
res.set('Content-Type', 'text/json');
res.status(200).send(JSON.stringify({ data: stdout, err:null }));
return ;
}
};
if(req.body["app"]==null)
app=null;
if (req.body["app"] != null && req.body["app"].length > 0){
if(req.body["app"] === "self")
app = $.project.getPackageName();
else
app = req.body["app"];
}else{
app = null;
}
// prepare intent
Logger.info("[WEBSERVER] Send intent whith data");
factory = new INTENT.IntentCommandFactory(
typeIntent,
app,
extraAdb);
device.exec(
factory.getIntentCommand(
new INTENT.Intent({
action: (req.body["action"]==null? null : req.body["action"]),
data_uri: (req.body["data_uri"]==null? null : req.body["data_uri"]),
mime_type: (req.body["mime_type"]==null? null : req.body["mime_type"]),
category: (req.body["category"]==null? null : req.body["category"]),
flags: (req.body["flags"]==null? null : req.body["flags"]),
extra_keys: (req.body["extraKeys"]==null? null : req.body["extraKeys"]),
extra_opts: (req.body["extraOpts"]==null? null : req.body["extraOpts"])
})
),
callbacks
);
// send command
//try{
/*
if(!custom){
Logger.info("[WEBSERVER] Send intent whith data");
msg = device.sendIntent({
data: (intentFilter.hasData()? data : null),
app: $.project.getPackageName()
},callbacks,intentFilter);
}else{
Logger.info("[WEBSERVER] Send custom intent");
// custom intent
msg = device.sendIntent({
data: data,
category: categ,
action: action,
app: $.project.getPackageName()
},callbacks,intentFilter);
}
Logger.info("[WEBSERVER] Intent sent (#2)");
/* }
if(msg.stderr!==null)
else
res.status(200).send(JSON.stringify({ data: msg.stdout, err:null }));
*/
/*}catch(excp){
Logger.error("[WEBSERVER] Intent exception : ",excp);
res.set('Content-Type', 'text/json');
res.status(404).send(JSON.stringify({ data: null, err: excp.messgae }));
}*/
});
}
/**
* @method
*/
showAccessLogs() {
let code;
for (let i = 0; this.logs.access.length; i++) {
code = this.logs.access[i].substr(0, 2);
if (code == "[4")
Logger.error(this.logs.access[i]);
else
Logger.success(this.logs.access[i]);
}
}
/**
* To use routes of install mode
*
* @method
*/
useInstallMode(){
this.initInstallRoutes();
}
/**
* To use routes of production mode
*
* @method
*/
useProductionMode(){
const projectDependentPath = [
'/api/hook',
'/api/probe',
'/api/find',
'/api/intent',
'/api/scanner',
'/api/field',
'/api/class',
'/api/finder',
'/api/package',
'/api/tags',
'/pages/index',
'/pages/finder',
'/pages/inspectors',
'/pages/probelog',
'/pages/probe',
'/pages/scanner',
'/pages/devicemanager',
'/inspectors/'
];
let self = this;
/**
* Redirect to /pages/splash.html if there is no project initialized
*/
this.app.use(function(req, res, next){
let f = false;
if(self.project != null){ next(); return; }
if(!req.url.startsWith('/pages/') && !req.url.startsWith('/inspectors/')){ next(); return; }
for(let i=0; i<projectDependentPath.length; i++){
if(req.url.startsWith(projectDependentPath[i])) {
f = true;
break;
}
}
if(f==false){ next(); return; }
res.redirect('/pages/splash.html');
res.send();
});
this.initRoutes();
this.uploader = Uploader.getInstance();
}
/**
* To start the web server
*
* @param {Integer} port Port number
* @method
*/
start(port) {
if (port == null) {
this.port = this.context.getConfiguration().getWebPort(); //project.config.web_port;
} else {
this.port = port;
}
let wwwPort = this.port;
this.context.printWebBanner(wwwPort);
this.app.listen(wwwPort, function () {
Logger.success('Server started on : ' + wwwPort);
});
}
}
module.exports = WebServer;