index.js 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260
  1. 'use strict';
  2. const Express = require('express');
  3. const mongoose = require('mongoose');
  4. const gplay = require('google-play-scraper');
  5. const path = require('path');
  6. var fs = require('fs');
  7. const qs = require('querystring');
  8. const sentiment = require('sentiment');
  9. const requestLib = require('request');
  10. const async = require('async');
  11. var _ = require('underscore-node');
  12. const models = require("../models");
  13. const router = Express.Router();
  14. function factorial(x) {
  15. if (x == 0) {
  16. return 1;
  17. }
  18. return x * factorial(x - 1);
  19. }
  20. function toList(apps) {
  21. return { results: apps };
  22. }
  23. function cleanUrls(req) {
  24. return function (app) {
  25. app.playstoreUrl = app.playstoreUrl || app.url;
  26. app.url = buildUrl(req, 'apps/' + app.appId);
  27. app.permissions = buildUrl(req, 'apps/' + app.appId + '/permissions');
  28. app.similar = buildUrl(req, 'apps/' + app.appId + '/similar');
  29. app.reviewsCount = app.reviewsCount || app.reviews;
  30. app.reviews = buildUrl(req, 'apps/' + app.appId + '/reviews');
  31. app.developer = {
  32. devId: app.developer.devId || app.developer,
  33. url: buildUrl(req, 'developers/' + qs.escape(app.developer.devId || app.developer))
  34. };
  35. return app;
  36. };
  37. }
  38. function saveAppData(app) {
  39. return new Promise(function (resolve, reject) {
  40. //console.log("app: " + app);
  41. app.save()
  42. .then(function (result) {
  43. resolve(app);
  44. })
  45. .catch(function (e) {
  46. console.log("error saving one app:" + JSON.stringify(e));
  47. if (e.code === 11000 || e.code === 11001) {
  48. models.app.findOne({ "appId": app.appId })
  49. .then(function (result) {
  50. resolve(result);
  51. })
  52. .catch(function (e) {
  53. console.log("error finding one app:" + JSON.stringify(e));
  54. reject(e);
  55. });
  56. } else {
  57. reject(e);
  58. }
  59. });
  60. });
  61. }
  62. function fetchReviewAndSave(opts) {
  63. return function (app) {
  64. opts.page = opts.page || 0;
  65. opts.throttle = 1;
  66. console.log("Initial page no:" + JSON.stringify(opts));
  67. return new Promise(function (resolve, reject) {
  68. async.whilst(
  69. function () {
  70. return opts.page != -1;
  71. },
  72. function (next) {
  73. console.log("Inside loop " + opts.page);
  74. gplay
  75. .reviews(opts)
  76. .then((reviews) => reviews.map(addSentiment()))
  77. .then(function (reviews) {
  78. console.log("Reviews length: " + reviews.length);
  79. //if (!reviews.length) { opts.page = -1 }
  80. return new Promise(function (resolve, reject) {
  81. async.mapSeries(reviews, function (review, callback) {
  82. review.app = app._id;
  83. new models.review(review).save()
  84. .then(function (result) {
  85. app.reviews.push(result._id);
  86. return callback(null, result);
  87. })
  88. .catch(function (e) {
  89. console.log("error saving one review:" + JSON.stringify(e));
  90. if (e.code === 11000 || e.code === 11001) {
  91. models.review.findOne({ "reviewId": review.reviewId })
  92. .then(function (result) {
  93. return callback(null, result);
  94. })
  95. .catch(function (e) {
  96. console.log("error finding one review:" + JSON.stringify(e));
  97. return callback(e, null);
  98. });
  99. } else {
  100. return callback(e, null);
  101. }
  102. //return callback(e, null);
  103. });
  104. }, function (err, results) {
  105. //console.log("Error map series: " + JSON.stringify(err));
  106. //console.log("Finished map series!!!");
  107. console.log("Map Series finish review length: " + results.length);
  108. resolve(results);
  109. });
  110. });
  111. })
  112. .then(function (reviews) {
  113. if (!reviews.length) { opts.page = -1; }
  114. if (opts.page == 50) { opts.page = -1; };
  115. if (opts.page != -1) { opts.page++ };
  116. //console.log("Reviews length2: " + reviews.length);
  117. //console.log("Return counter1: " + opts.page);
  118. next();
  119. })
  120. .catch(function (e) {
  121. opts.page = -1;
  122. });
  123. },
  124. function (err) {
  125. if (err) {
  126. console.log("Error async:" + err);
  127. reject(err);
  128. } else {
  129. console.log("Finished!!!");
  130. //console.log("App reviews: " + JSON.stringify(app.reviews));
  131. resolve(app);
  132. }
  133. }
  134. );
  135. });
  136. };
  137. }
  138. function fetchPermissions(opts) {
  139. return function (app) {
  140. return new Promise(function (resolve, reject) {
  141. gplay.permissions(opts)
  142. .then(function (permissions) {
  143. app.permissions = permissions;
  144. resolve(app);
  145. })
  146. .catch(function (e) {
  147. reject(e);
  148. });
  149. });
  150. }
  151. }
  152. function saveReviewData(app) {
  153. return function (review) {
  154. review.app = app._id;
  155. //review.app = "";
  156. console.log("review: " + review.userName);
  157. requestLib({
  158. uri: "http://localhost:3000/db/review/",
  159. method: "POST",
  160. json: review
  161. }, function (error, response, body) {
  162. if (!error) {
  163. review._id = body._id;
  164. }
  165. });
  166. return review;
  167. };
  168. }
  169. function addSentiment() {
  170. return function (app) {//receives a single review actually
  171. const sentMin = -5;
  172. const sentMax = 5;
  173. app.sentiment = sentiment(app.text);
  174. app.sentiment.sentimentProportional = app.sentiment.score / Math.max(Math.sqrt(app.sentiment.tokens.length), app.sentiment.words.length);
  175. app.sentiment.sentimentScaled = (app.sentiment.sentimentProportional + Math.abs(0 - sentMin)) / (sentMax - sentMin);
  176. app.sentiment.sentimentConfidence = 1 - Math.abs(((app.score - 1) / 4) - app.sentiment.sentimentScaled);
  177. app.sentiment.scaledReviewRating = (app.score - 1) / 4;
  178. // app.sentiment.sentimentConfidence = 1 - Math.abs(app.score - app.sentiment.sentimentScaled);
  179. app.reviewId = qs.parse(app.url).reviewId || "";
  180. return app;
  181. };
  182. }
  183. function isAlreadyDownloaded(apps) {
  184. return new Promise(function (resolve, reject) {
  185. models.app.find({})
  186. .then((downloadedApps) => downloadedApps.map(function (app) {
  187. var currentApp = _(apps).findWhere({ appId: app.appId });//_.findWhere(apps, {appId: app.appId});
  188. //console.log("cur:" + currentApp);
  189. if (currentApp != undefined) {
  190. currentApp.isAlreadyDownloaded = "yes";
  191. }
  192. }))
  193. .then(function (downloadedApps) {
  194. resolve(apps);
  195. //console.log(apps);
  196. });
  197. });
  198. }
  199. function buildUrl(req, subpath) {
  200. return req.protocol + '://' + path.join(req.get('host'), req.baseUrl, subpath);
  201. }
  202. /* Index */
  203. router.get('/', function (req, res) {
  204. res.json({
  205. apps: buildUrl(req, 'apps'),
  206. developers: buildUrl(req, 'developers')
  207. });
  208. });
  209. /* App search */
  210. router.get('/apps/', function (req, res, next) {
  211. console.log('Inside app search1');
  212. if (!req.query.q) {
  213. return next();
  214. }
  215. console.log('Inside app search2');
  216. const opts = Object.assign({ term: req.query.q }, req.query);
  217. gplay.search(opts)
  218. .then((apps) => apps.map(cleanUrls(req)))
  219. .then(isAlreadyDownloaded)
  220. .then(toList)
  221. .then(res.json.bind(res))
  222. .catch(next);
  223. });
  224. /* Search suggest */
  225. router.get('/apps/', function (req, res, next) {
  226. console.log('Inside search suggest1');
  227. if (!req.query.suggest) {
  228. return next();
  229. }
  230. console.log('Inside search suggest2');
  231. function toJSON(term) {
  232. return {
  233. term,
  234. url: buildUrl(req, '/apps/') + '?' + qs.stringify({ q: term })
  235. };
  236. }
  237. gplay.suggest({ term: req.query.suggest })
  238. .then((terms) => terms.map(toJSON))
  239. .then(toList)
  240. .then(res.json.bind(res))
  241. .catch(next);
  242. });
  243. /* App list */
  244. router.get('/apps/', function (req, res, next) {
  245. console.log('Inside App list1');
  246. function paginate(apps) {
  247. const num = parseInt(req.query.num || '60');
  248. const start = parseInt(req.query.start || '0');
  249. if (start - num >= 0) {
  250. req.query.start = start - num;
  251. apps.prev = buildUrl(req, '/apps/') + '?' + qs.stringify(req.query);
  252. }
  253. if (start + num <= 500) {
  254. req.query.start = start + num;
  255. apps.next = buildUrl(req, '/apps/') + '?' + qs.stringify(req.query);
  256. }
  257. return apps;
  258. }
  259. console.log('Inside App list2');
  260. gplay.list(req.query)
  261. .then((apps) => apps.map(cleanUrls(req)))
  262. .then(toList).then(paginate)
  263. .then(res.json.bind(res))
  264. .catch(next);
  265. });
  266. /* App detail*/
  267. router.get('/apps/:appId', function (req, res, next) {
  268. const opts = Object.assign({ appId: req.params.appId }, req.query);
  269. gplay.app(opts)
  270. .then(cleanUrls(req))
  271. .then(function (app) {
  272. app.reviews = [];
  273. app.permissions = [];
  274. return new models.app(app);
  275. })
  276. //.then(saveAppData)//app data without permissions and reviews
  277. //.then(fetchReviewAndSave(opts))
  278. //.then(saveAppData)//save with reviews array
  279. //.then(fetchPermissions(opts))
  280. //.then(saveAppData)//save with permissions array
  281. .then(res.json.bind(res))
  282. .catch(next);
  283. });
  284. /* Similar apps */
  285. router.get('/apps/:appId/similar', function (req, res, next) {
  286. const opts = Object.assign({ appId: req.params.appId }, req.query);
  287. gplay.similar(opts)
  288. .then((apps) => apps.map(cleanUrls(req)))
  289. .then(toList)
  290. .then(res.json.bind(res))
  291. .catch(next);
  292. });
  293. /* App permissions */
  294. router.get('/apps/:appId/permissions', function (req, res, next) {
  295. const opts = Object.assign({ appId: req.params.appId }, req.query);
  296. gplay.permissions(opts)
  297. .then(toList)
  298. .then(res.json.bind(res))
  299. .catch(next);
  300. });
  301. /* App reviews */
  302. router.get('/apps/:appId/reviews', function (req, res, next) {
  303. function paginate(apps) {
  304. const page = parseInt(req.query.page || '0');
  305. const subpath = '/apps/' + req.params.appId + '/reviews/';
  306. if (page > 0) {
  307. req.query.page = page - 1;
  308. apps.prev = buildUrl(req, subpath) + '?' + qs.stringify(req.query);
  309. }
  310. if (apps.results.length) {
  311. req.query.page = page + 1;
  312. apps.next = buildUrl(req, subpath) + '?' + qs.stringify(req.query);
  313. }
  314. return apps;
  315. }
  316. const opts = Object.assign({ appId: req.params.appId }, req.query);
  317. gplay.reviews(opts)
  318. .then((reviews) => reviews.map(addSentiment()))
  319. .then(toList).then(paginate)
  320. .then(res.json.bind(res))
  321. .catch(next);
  322. });
  323. /* Apps by developer */
  324. router.get('/developers/:devId/', function (req, res, next) {
  325. const opts = Object.assign({ devId: req.params.devId }, req.query);
  326. gplay.developer(opts)
  327. .then((apps) => apps.map(cleanUrls(req)))
  328. .then((apps) => ({
  329. devId: req.params.devId,
  330. apps
  331. }))
  332. .then(res.json.bind(res))
  333. .catch(next);
  334. });
  335. /* Developer list (not supported) */
  336. router.get('/developers/', function (req, res) {
  337. res.status(400).json({
  338. message: 'Please specify a developer id.',
  339. example: buildUrl(req, '/developers/' + qs.escape('DxCo Games'))
  340. });
  341. });
  342. function errorHandler(err, req, res, next) {
  343. //res.status(400).json({ message: err.message + "\n" + err.stack.split("\n") });
  344. res.status(400).json({ message: err.message });
  345. next();
  346. }
  347. /* Test synchronous operation */
  348. router.get('/TestSync/', function (req, res, next) {
  349. const opts = Object.assign({ appId: req.params.appId, counter: 0 }, req.query);
  350. /*for (var i = 0; i < 20; i++) {
  351. gplay.reviews({
  352. appId: 'shree.hindi.gymguide',
  353. page: i,
  354. sort: gplay.sort.NEWEST,
  355. throttle: 5
  356. }).then(function (apps) {
  357. console.log("Return data length: "+ apps.length);
  358. }).catch(function (e) {
  359. console.log('There was an error fetching the reviews!');
  360. });
  361. }*/
  362. /*for (var i = 0; i < 5; i++) {
  363. doRequest({
  364. method: 'GET',
  365. uri: "http://localhost:3000/db/app/",
  366. json: true
  367. }).then(function (body) {
  368. console.log("Return id2: " + body[0]._id);
  369. });
  370. }*/
  371. /*async.whilst(
  372. function () {
  373. return opts.counter != 5;// && count < 5;
  374. },
  375. function (next) {
  376. console.log("Inside loop " + opts.counter);
  377. doRequest({
  378. method: 'GET',
  379. uri: "http://localhost:3000/db/app/",
  380. json: true
  381. }).then(function (body) {
  382. console.log("Return id1: " + body[0]._id);
  383. console.log("Return counter1: " + opts.counter);
  384. return body;
  385. }).then(function (body) {
  386. opts.counter++;
  387. console.log("Return id2: " + body[0]._id);
  388. console.log("Return counter2: " + opts.counter);
  389. next();
  390. });
  391. },
  392. function (err) {
  393. if (err) {
  394. console.log("Error async:" + err);
  395. } else {
  396. console.log("Finished!!!");
  397. }
  398. }
  399. );*/
  400. const reviewOpts = {
  401. appId: 'de.datenlotsen.campusnet.tuda',
  402. page: 0,
  403. sort: gplay.sort.NEWEST,
  404. throttle: 5
  405. };
  406. async.whilst(
  407. function () {
  408. return reviewOpts.page != -1;// && count < 5;
  409. },
  410. function (next) {
  411. console.log("Inside loop " + reviewOpts.page);
  412. gplay
  413. .reviews(reviewOpts)
  414. .then((reviews) => reviews.map(addSentiment()))
  415. .then(function (reviews) {
  416. console.log("Reviews length: " + reviews.length);
  417. if (!reviews.length) { reviewOpts.page = -1 }
  418. /*return doRequest({
  419. method: 'GET',
  420. uri: "http://localhost:3000/db/app/",
  421. json: true
  422. });*/
  423. /*return new Promise(function (resolve, reject) {
  424. reviews.map(function (review) {
  425. doRequest({
  426. method: 'GET',
  427. uri: "http://localhost:3000/db/app/",
  428. json: true
  429. }).then(function (body) {
  430. console.log("Return of Get requests: " + body[0]._id);
  431. });
  432. });
  433. resolve(reviews);
  434. });*/
  435. return new Promise(function (resolve, reject) {
  436. async.mapSeries(reviews, function (review, callback) {
  437. review.app = "582b633d8366b306c992c67f";
  438. console.log("Inside map series");
  439. doRequest({
  440. method: "POST",
  441. uri: "http://localhost:3000/db/review/",
  442. json: review
  443. }).then(function (body) {
  444. console.log("Return of Post requests: " + body._id);
  445. return callback(null, body);
  446. });
  447. }, function (err, results) {
  448. console.log("Finished map series!!!");
  449. console.log("Map Series finish review length: " + results.length);
  450. resolve(results);
  451. });
  452. });
  453. })
  454. /*.then(function (body) {
  455. console.log("Return id1: " + body[0]._id);
  456. console.log("Return counter1: " + reviewOpts.page);
  457. return body;
  458. })
  459. .then(function (body) {
  460. if (reviewOpts.page != -1) { reviewOpts.page++ };
  461. console.log("Return id2: " + body[0]._id);
  462. console.log("Return counter2: " + reviewOpts.page);
  463. next();
  464. })*/
  465. .then(function (reviews) {
  466. if (reviewOpts.page != -1) { reviewOpts.page++ };
  467. console.log("Reviews length2: " + reviews.length);
  468. console.log("Return counter1: " + reviewOpts.page);
  469. next();
  470. });
  471. },
  472. function (err) {
  473. if (err) {
  474. console.log("Error async:" + err);
  475. } else {
  476. console.log("Finished!!!");
  477. }
  478. }
  479. );
  480. res.json({ hi: "Finished!" });
  481. });
  482. router.get('/ShowAgg/:appId', function (req, res, next) {//Test Aggregation
  483. const opts = Object.assign({ appId: req.params.appId, counter: 0 }, req.query);
  484. models.app.findOne({ "appId": opts.appId })
  485. .then(function (result) {
  486. return result.toObject();
  487. })
  488. .then(function (app) {
  489. return new Promise(function (resolve, reject) {
  490. models.review.aggregate([
  491. {
  492. $match: {
  493. app: app._id
  494. }
  495. },
  496. {
  497. $group: {
  498. _id: "$app",
  499. total_reviews_confidence: { $sum: "$sentiment.sentimentConfidence" },
  500. total_reviews: { $sum: 1 }
  501. }
  502. }],
  503. function (err, res) {
  504. if (err) {
  505. console.log("err: " + JSON.stringify(err));
  506. reject(err);
  507. }
  508. console.log("total: " + JSON.stringify(res[0]));
  509. console.log("average: " + res[0].total_reviews_confidence / res[0].total_reviews);
  510. app.averageReviewConfidence = res[0].total_reviews_confidence / res[0].total_reviews;
  511. resolve(app);
  512. }
  513. );
  514. });
  515. //return app;
  516. })
  517. .then(res.json.bind(res))
  518. .catch(function (e) {
  519. console.log("error finding one app:" + JSON.stringify(e));
  520. next();
  521. });
  522. //res.json({ hi: "Finished!" });
  523. });
  524. /* Test end */
  525. router.get('/downloadedapps/', function (req, res, next) {
  526. var filter = {};
  527. if(req.query.genre) {
  528. filter.genreId = { $regex: '.*' + req.query.genre + '.*' };
  529. }
  530. models.app.find(filter)
  531. .then((apps) => apps.map(function (app) {
  532. app.url = "http://localhost:3000/crawler/downloadedapps/" + app.appId;
  533. return app;
  534. }))
  535. .then(res.json.bind(res))
  536. .catch(function (e) {
  537. console.log("error fetching downloaded apps:" + JSON.stringify(e));
  538. next();
  539. });
  540. });
  541. router.get('/downloadedapps/:appId', function (req, res, next) {
  542. const opts = Object.assign({ appId: req.params.appId }, req.query);
  543. const posture = parseInt(req.query.posture || '0');
  544. var filter = {
  545. "appId": opts.appId
  546. };
  547. models.app.findOne(filter)
  548. .then(function (result) {
  549. return result.toObject();
  550. })
  551. .then(function (app) {
  552. return new Promise(function (resolve, reject) {
  553. models.review.aggregate([
  554. {
  555. $match: {
  556. app: app._id
  557. }
  558. },
  559. {
  560. $group: {
  561. _id: "$app",
  562. total_reviews_confidence: { $sum: "$sentiment.sentimentConfidence" },
  563. review_rating_sum: { $sum: "$sentiment.scaledReviewRating" },
  564. total_reviews: { $sum: 1 },
  565. total_ratingValue: { $sum: "$score" }
  566. }
  567. }],
  568. function (err, res) {
  569. if (err) {
  570. console.log("err: " + JSON.stringify(err));
  571. reject(err);
  572. }
  573. console.log("reviews total: " + JSON.stringify(res[0]));
  574. console.log("average: " + res[0].total_reviews_confidence / res[0].total_reviews);
  575. //source: http://stackoverflow.com/questions/929103/convert-a-number-range-to-another-range-maintaining-ratio
  576. //(((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin
  577. app.reviewsTrustValue = ((((res[0].total_ratingValue / res[0].total_reviews) - 1) * (1 - 0)) / (5 - 1)) + 0;
  578. app.reviewsConfidenceValue = res[0].total_reviews_confidence / res[0].total_reviews;//averageReviewConfidence
  579. app.reviewMetric = res[0].review_rating_sum / res[0].total_reviews;
  580. resolve(app);
  581. }
  582. );
  583. });
  584. })
  585. .then(function (app) {
  586. return new Promise(function (resolve, reject) {
  587. models.app.aggregate([
  588. {
  589. $project: {
  590. genreId: 1,
  591. permissions_count: { $size: "$permissions" }
  592. }
  593. },
  594. {
  595. $match: {
  596. genreId: app.genreId
  597. }
  598. },
  599. {
  600. $group: {
  601. _id: "$genreId",
  602. same_genre_permissions_count: { $sum: "$permissions_count" },
  603. same_genre_count: { $sum: 1 }
  604. }
  605. }],
  606. function (err, res) {
  607. if (err) {
  608. console.log("err: " + JSON.stringify(err));
  609. reject(err);
  610. }
  611. //console.log("permissions total: " + JSON.stringify(res));
  612. app.categoryAveragePermission = Math.floor(res[0].same_genre_permissions_count / res[0].same_genre_count);
  613. console.log("categoryAveragePermission: " + app.categoryAveragePermission);
  614. let powerValue = app.permissions.length - app.categoryAveragePermission;
  615. app.permissionsTrustValue = (((app.score - 1) * (1 - 0)) / (5 - 1)) + 0;
  616. app.cofidenceInNumberofPermissions = (factorial(app.categoryAveragePermission) / factorial(app.permissions.length)) * Math.pow(app.categoryAveragePermission, powerValue);
  617. resolve(app);
  618. }
  619. );
  620. });
  621. })
  622. .then(function (app) {
  623. return new Promise(function (resolve, reject) {
  624. models.app.find({ "genreId": app.genreId })
  625. .then(function (results) {
  626. app.genreCount = results.length;
  627. resolve(app);
  628. })
  629. .catch(function (err) {
  630. reject(err);
  631. });
  632. });
  633. })
  634. .then(function (app) {
  635. console.log(app.histogram["1"]);
  636. let totalRatingsSum = (app.histogram["1"] * 1) + (app.histogram["2"] * 2) + (app.histogram["3"] * 3) + (app.histogram["4"] * 1) + (app.histogram["5"] * 5);
  637. let averageRating = totalRatingsSum / app.reviewsCount;
  638. console.log("rc: " + app.reviewsCount);
  639. console.log("av: " + averageRating);
  640. console.log("score: " + app.score);
  641. let wmd = 0;
  642. if (app.histogram["1"] + app.histogram["2"] + app.histogram["3"] == 0) {
  643. wmd = ((app.histogram["4"] * Math.abs(averageRating - 4)) / app.reviewsCount) + ((app.histogram["5"] * Math.abs(averageRating - 5)) / app.reviewsCount);
  644. } else if (app.histogram["3"] + app.histogram["4"] + app.histogram["5"] == 0) {
  645. wmd = ((app.histogram["1"] * Math.abs(averageRating - 1)) / app.reviewsCount) + ((app.histogram["2"] * Math.abs(averageRating - 2)) / app.reviewsCount);
  646. } else {
  647. let lowerRatingCount = app.histogram["1"] + app.histogram["2"] + app.histogram["3"];
  648. let upperRatingCount = app.histogram["5"] + app.histogram["4"] + app.histogram["3"];
  649. let lowerAverageRating = ((app.histogram["1"] * 1) + (app.histogram["2"] * 2) + (app.histogram["3"] * 3)) / lowerRatingCount;
  650. console.log("lowerAverageRating: " + lowerAverageRating);
  651. let upperAverageRating = ((app.histogram["5"] * 5) + (app.histogram["4"] * 4) + (app.histogram["3"] * 3)) / upperRatingCount;
  652. console.log("upperAverageRating: " + upperAverageRating);
  653. let lowerWeight = lowerRatingCount / (lowerRatingCount + upperRatingCount);
  654. console.log("lowerWeight: " + lowerWeight);
  655. let upperWeight = upperRatingCount / (lowerRatingCount + upperRatingCount);
  656. console.log("upperWeight: " + upperWeight);
  657. wmd = (lowerWeight * Math.abs(averageRating - lowerAverageRating)) + (upperWeight * Math.abs(averageRating - upperAverageRating));
  658. console.log("wmd: " + wmd);
  659. }
  660. app.averageRatingTrustValue = (((app.score - 1) * (1 - 0)) / (5 - 1)) + 0;
  661. app.averageRatingConfidenceValue = 1 - (wmd / 2);
  662. console.log("averageRatingConfidenceValue: " + app.averageRatingConfidenceValue);
  663. return app;
  664. })
  665. .then(function(app) {
  666. let sLength = 25;
  667. let pLength = 9;
  668. let sPositiveEvidences = 0;
  669. let sNegativeEvidences = 0;
  670. let pPositiveEvidences = 0;
  671. let pNegativeEvidences = 0;
  672. if(posture) {
  673. sLength--;
  674. pLength--;
  675. }
  676. _(app.appicaptor.indicator).each(function (element, index) {
  677. //console.log(element.attr);
  678. //console.log(index);//just the index 0....n
  679. if (element.attr.text == "Client communication used?") {
  680. if (element.attr.value == "yes") {
  681. sNegativeEvidences++;
  682. } else {
  683. sPositiveEvidences++;
  684. }
  685. }
  686. if (element.attr.text == "SSL/TLS used?") {
  687. if (element.attr.value == "no") {
  688. sNegativeEvidences++;
  689. } else {
  690. sPositiveEvidences++;
  691. }
  692. }
  693. if (element.attr.text == "Domains accessed with http AND https: ") {
  694. if (element.resultList[0].result.length > 0) {
  695. sNegativeEvidences++;
  696. } else {
  697. sPositiveEvidences++;
  698. }
  699. }
  700. if (element.attr.text == "Custom SSL/TLS trust manager implemented?") {
  701. if (element.attr.value == "yes") {
  702. sNegativeEvidences++;
  703. } else {
  704. sPositiveEvidences++;
  705. }
  706. }
  707. if (element.attr.text == "Faulty custom SSL/TLS trust manager implemented?") {
  708. if (element.attr.value == "yes") {
  709. sNegativeEvidences++;
  710. } else {
  711. sPositiveEvidences++;
  712. }
  713. }
  714. if (element.attr.text == "SSL/TLS using custom error handling?") {
  715. if (element.attr.value == "yes") {
  716. sNegativeEvidences++;
  717. } else {
  718. sPositiveEvidences++;
  719. }
  720. }
  721. if (element.attr.text == "SSL/TLS using faulty custom error handling?") {
  722. if (element.attr.value == "yes") {
  723. sNegativeEvidences++;
  724. } else {
  725. sPositiveEvidences++;
  726. }
  727. }
  728. if (element.attr.text == "SSL/TLS using manual domain name verification?") {
  729. if (element.attr.value == "yes") {
  730. sNegativeEvidences++;
  731. } else {
  732. sPositiveEvidences++;
  733. }
  734. }
  735. if (element.attr.text == "Unprotected communication?") {
  736. if (element.attr.value == "yes") {
  737. sNegativeEvidences++;
  738. } else {
  739. sPositiveEvidences++;
  740. }
  741. }
  742. if (element.attr.text == "Unprotected HTML?") {
  743. if (element.attr.value == "yes") {
  744. sNegativeEvidences++;
  745. } else {
  746. sPositiveEvidences++;
  747. }
  748. }
  749. if (element.attr.text == "Cryptographic Primitives: ") {
  750. if (element.resultList[0].result.length > 0) {
  751. sNegativeEvidences++;
  752. } else {
  753. sPositiveEvidences++;
  754. }
  755. }
  756. if (element.attr.text == "Application needs dangerous permissions? ") {
  757. if (element.attr.value == "yes") {
  758. sNegativeEvidences++;
  759. } else {
  760. sPositiveEvidences++;
  761. }
  762. }
  763. if (element.attr.text == "JavaScript to SDK API bridge usage?") {
  764. if (element.attr.value == "yes") {
  765. sNegativeEvidences++;
  766. } else {
  767. sPositiveEvidences++;
  768. }
  769. }
  770. if (element.attr.text == "Is application overprivileged?" && !posture) {
  771. if (element.attr.value == "yes") {
  772. sNegativeEvidences++;
  773. } else {
  774. sPositiveEvidences++;
  775. }
  776. }
  777. if (element.attr.text == "Userdefined permission usage: ") {
  778. if (element.resultList[0].result.length > 0) {
  779. sNegativeEvidences++;
  780. } else {
  781. sPositiveEvidences++;
  782. }
  783. }
  784. if (element.attr.text == "WiFi-Direct enabled?") {
  785. if (element.attr.value == "yes") {
  786. sNegativeEvidences++;
  787. } else {
  788. sPositiveEvidences++;
  789. }
  790. }
  791. if (element.attr.text == "App can handle documents of mimeType: ") {
  792. if (element.attr.value == "none") {
  793. sPositiveEvidences++;
  794. } else {
  795. sNegativeEvidences++;
  796. }
  797. }
  798. if (element.attr.text == "Screenshot protection used?") {
  799. if (element.attr.value == "yes") {
  800. sPositiveEvidences++;
  801. } else {
  802. sNegativeEvidences++;
  803. }
  804. }
  805. if (element.attr.text == "Tap Jacking Protection used?") {
  806. if (element.attr.value == "yes") {
  807. sPositiveEvidences++;
  808. } else {
  809. sNegativeEvidences++;
  810. }
  811. }
  812. if (element.attr.text == "Scheduled Alarm Manager registered?") {
  813. if (element.attr.value == "no") {
  814. sPositiveEvidences++;
  815. } else {
  816. sNegativeEvidences++;
  817. }
  818. }
  819. if (element.attr.text == "Dynamically loaded code at runtime?") {
  820. if (element.attr.value == "no") {
  821. sPositiveEvidences++;
  822. } else {
  823. sNegativeEvidences++;
  824. }
  825. }
  826. if (element.attr.text == "Allow app debugging Flag?") {
  827. if (element.attr.value == "no") {
  828. sPositiveEvidences++;
  829. } else {
  830. sNegativeEvidences++;
  831. }
  832. }
  833. if (element.attr.text == "Allow autoexecute after Phone Reboot?") {
  834. if (element.attr.value == "no") {
  835. sPositiveEvidences++;
  836. } else {
  837. sNegativeEvidences++;
  838. }
  839. }
  840. if (element.attr.text == "App uses outdated signature key?") {
  841. if (element.attr.value == "no") {
  842. sPositiveEvidences++;
  843. } else {
  844. sNegativeEvidences++;
  845. }
  846. }
  847. if (element.attr.text == "Contains native libraries: ") {
  848. if (element.attr.value == "no") {
  849. sPositiveEvidences++;
  850. } else {
  851. sNegativeEvidences++;
  852. }
  853. }
  854. //privacy
  855. if (element.attr.text == "Obfuscation used?") {
  856. if (element.attr.value == "yes") {
  857. pPositiveEvidences++;
  858. } else {
  859. pNegativeEvidences++;
  860. }
  861. }
  862. if (element.attr.text == "Device administration policy entries: ") {
  863. if (element.attr.value == "none") {
  864. pPositiveEvidences++;
  865. } else {
  866. pNegativeEvidences++;
  867. }
  868. }
  869. if (element.attr.text == "Accessed unique identifier(s): ") {
  870. if (element.resultList[0].result.length > 0) {
  871. pNegativeEvidences++;
  872. } else {
  873. pPositiveEvidences++;
  874. }
  875. }
  876. if (element.attr.text == "Advertisment-/tracking frameworks found: " && !posture) {
  877. if (element.attr.value == "none") {
  878. pPositiveEvidences++;
  879. } else if (element.resultList[0].result.length > 10) {
  880. pNegativeEvidences++;
  881. } else {
  882. pPositiveEvidences++;
  883. }
  884. }
  885. if (element.attr.text == "App provides public accessible activities?") {
  886. if (element.attr.value == "yes") {
  887. pNegativeEvidences++;
  888. } else {
  889. pPositiveEvidences++;
  890. }
  891. }
  892. if (element.attr.text == "Backup of app is allowed?") {
  893. if (element.attr.value == "yes") {
  894. pNegativeEvidences++;
  895. } else {
  896. pPositiveEvidences++;
  897. }
  898. }
  899. if (element.attr.text == "Log Statement Enabled?") {
  900. if (element.attr.value == "yes") {
  901. pNegativeEvidences++;
  902. } else {
  903. pPositiveEvidences++;
  904. }
  905. }
  906. if (element.attr.text == "Permission to access address book?") {
  907. if (element.attr.value == "yes") {
  908. pNegativeEvidences++;
  909. } else {
  910. pPositiveEvidences++;
  911. }
  912. }
  913. if (element.attr.text == "Unprotected preference files found?") {
  914. if (element.attr.value == "yes") {
  915. pNegativeEvidences++;
  916. } else {
  917. pPositiveEvidences++;
  918. }
  919. }
  920. })
  921. console.log("sPositiveEvidences " + sPositiveEvidences);
  922. console.log("sNegativeEvidences " + sNegativeEvidences);
  923. console.log("pPositiveEvidences " + pPositiveEvidences);
  924. console.log("pNegativeEvidences " + pNegativeEvidences);
  925. app.sTrustValue = sPositiveEvidences/(sPositiveEvidences+sNegativeEvidences);
  926. app.sConfidenceValue = (sLength*(sPositiveEvidences+sNegativeEvidences))/((2*(sLength-(sPositiveEvidences+sNegativeEvidences)))+(sLength*(sPositiveEvidences+sNegativeEvidences)));
  927. app.pTrustValue = pPositiveEvidences/(pPositiveEvidences+pNegativeEvidences);
  928. app.pConfidenceValue = (pLength*(pPositiveEvidences+pNegativeEvidences))/((2*(pLength-(pPositiveEvidences+pNegativeEvidences)))+(pLength*(pPositiveEvidences+pNegativeEvidences)));
  929. return app;
  930. })
  931. .then(res.json.bind(res))
  932. .catch(function (e) {
  933. console.log("error fetching downloaded app: " + JSON.stringify(e));
  934. next();
  935. });
  936. });
  937. function doRequest(opts) { //will be used in later phase may be
  938. return new Promise((resolve, reject) => requestLib(opts, function (error, response, body) {
  939. if (error) {
  940. return reject(error);
  941. }
  942. if (response.statusCode >= 400) {
  943. return reject({ response });
  944. }
  945. resolve(body);
  946. }));
  947. }
  948. router.get('/insertAppicaptorData/:appId', function (req, res, next) {
  949. var obj = JSON.parse(fs.readFileSync('./appicaptor/outputall.json', 'utf8'));
  950. var outputObj = {
  951. overprivilegedPermission: {},
  952. dangerousPermission: {},
  953. extensiveTrackingFrameworkUsage: {},
  954. outdatedCryptographicPrimitives: {},
  955. unprotectedCommunication: {}
  956. };
  957. for (var i = 0; i < obj.report.app.length; i++) {
  958. //console.log("ff: " + obj.report.app[i].$.appId.split(":")[0]);
  959. //console.log("ll: " + req.params.appId);
  960. if (obj.report.app[i].attr.appId.split(":")[0] == req.params.appId) {
  961. //console.log("isnersfsdf");
  962. for (var j = 0; j < obj.report.app[i].indicator.length; j++) {
  963. //do something with obj[i]
  964. for (var ind in obj.report.app[i].indicator[j]) {
  965. for (var vals in obj.report.app[i].indicator[j][ind]) {
  966. //Privacy Risks
  967. if (obj.report.app[i].indicator[j][ind][vals] == "Redundant permission correlation") {
  968. outputObj.overprivilegedPermission.Value = obj.report.app[i].indicator[j][ind]["value"];
  969. }
  970. if (obj.report.app[i].indicator[j][ind][vals] == "Overprivileged permission yes/no") {
  971. if (obj.report.app[i].indicator[j]["resultList"] != undefined) {
  972. outputObj.overprivilegedPermission.List = obj.report.app[i].indicator[j]["resultList"][0].result;
  973. }
  974. }
  975. if (obj.report.app[i].indicator[j][ind][vals] == "Dangerous Permission") {
  976. outputObj.dangerousPermission.Value = obj.report.app[i].indicator[j][ind]["value"];
  977. if (obj.report.app[i].indicator[j]["detailList"] != undefined) {
  978. outputObj.dangerousPermission.List = obj.report.app[i].indicator[j]["detailList"][0].detail;
  979. }
  980. }
  981. if (obj.report.app[i].indicator[j][ind][vals] == "privacy-risk") {
  982. outputObj.extensiveTrackingFrameworkUsage.Value = obj.report.app[i].indicator[j][ind]["value"];
  983. if (obj.report.app[i].indicator[j]["detailList"] != undefined) {
  984. outputObj.extensiveTrackingFrameworkUsage.Reason = obj.report.app[i].indicator[j]["detailList"][0].detail;
  985. }
  986. }
  987. if (obj.report.app[i].indicator[j][ind][vals] == "Advertisment/Tracking Usage") {
  988. if (obj.report.app[i].indicator[j]["resultList"] != undefined) {
  989. outputObj.extensiveTrackingFrameworkUsage.List = obj.report.app[i].indicator[j]["resultList"][0].result;
  990. }
  991. }
  992. //Security Risks
  993. if (obj.report.app[i].indicator[j][ind][vals] == "Cryptographic Primitives: ") {
  994. outputObj.outdatedCryptographicPrimitives.Value = obj.report.app[i].indicator[j][ind]["value"];
  995. if (obj.report.app[i].indicator[j]["resultList"] != undefined) {
  996. outputObj.outdatedCryptographicPrimitives.List = obj.report.app[i].indicator[j]["resultList"][0].result;
  997. }
  998. }
  999. if (obj.report.app[i].indicator[j][ind][vals] == "HTTP Access") {
  1000. outputObj.unprotectedCommunication.Value = obj.report.app[i].indicator[j][ind]["value"];
  1001. if (obj.report.app[i].indicator[j]["detailList"] != undefined) {
  1002. outputObj.unprotectedCommunication.List = obj.report.app[i].indicator[j]["detailList"][0].detail;
  1003. }
  1004. }
  1005. }
  1006. }
  1007. }
  1008. }
  1009. }
  1010. models.app.findOne({ "appId": req.params.appId })
  1011. .then(function (result) {
  1012. //console.log(outputObj);
  1013. result.appicaptor = outputObj;
  1014. return result;
  1015. })
  1016. .then(saveAppData)
  1017. .then(res.json.bind(res))
  1018. .catch(function (e) {
  1019. console.log("error fetching downloaded apps:" + JSON.stringify(e));
  1020. next();
  1021. });
  1022. });
  1023. router.get('/getDownloadInfo/:appId', function (req, res, next) {//if no data is avaiable it will first download app's data first
  1024. const opts = Object.assign({ appId: req.params.appId }, req.query);
  1025. models.app.findOne({ "appId": opts.appId })
  1026. .then(function (result) {
  1027. //console.log(result);
  1028. if (result) {
  1029. return result.toObject();
  1030. } else {
  1031. //return null;
  1032. return new Promise(function (resolve, reject) {
  1033. gplay.app(opts)
  1034. .then(cleanUrls(req))
  1035. .then(function (app) {
  1036. app.reviews = [];
  1037. app.permissions = [];
  1038. return new models.app(app);
  1039. })
  1040. .then(saveAppData)
  1041. .then(function(app) {
  1042. resolve(app);
  1043. })
  1044. .catch(function(e) {
  1045. reject(e);
  1046. });
  1047. });
  1048. }
  1049. })
  1050. .then(res.json.bind(res));
  1051. });
  1052. router.get('/getReviews/:appId', function (req, res, next) {
  1053. const opts = Object.assign({ appId: req.params.appId }, req.query);
  1054. models.app.findOne({ "appId": opts.appId })
  1055. .then(function (result) {
  1056. //console.log(result);
  1057. return result;
  1058. })
  1059. .then(fetchReviewAndSave(opts))
  1060. .then(saveAppData)
  1061. .then(res.json.bind(res));
  1062. });
  1063. //not used
  1064. router.get('/getReviewsFromGoogle/:appId', function (req, res, next) {
  1065. const opts = Object.assign({ appId: req.params.appId }, req.query);
  1066. opts.page = parseInt(req.query.page || '0');
  1067. gplay.reviews(opts)
  1068. .then(toList)
  1069. .then(res.json.bind(res))
  1070. .catch(next);
  1071. });
  1072. //not used
  1073. router.get('/fetchReview/:appId', function (req, res, next) {
  1074. const opts = Object.assign({ appId: req.params.appId }, req.query);
  1075. opts.page = parseInt(req.query.page || '0');
  1076. console.log("review page no:" + opts.page);
  1077. models.app.findOne({ "appId": opts.appId })
  1078. .then(function (result) {
  1079. return result;
  1080. })
  1081. .then(function (app) {
  1082. return new Promise(function (resolve, reject) {
  1083. gplay
  1084. .reviews(opts)
  1085. .then((reviews) => reviews.map(addSentiment()))
  1086. .then(function (reviews) {
  1087. console.log("Reviews length: " + reviews.length);
  1088. return new Promise(function (resolve, reject) {
  1089. async.mapSeries(reviews, function (review, callback) {
  1090. review.app = app._id;
  1091. new models.review(review).save()
  1092. .then(function (result) {
  1093. app.reviews.push(result._id);
  1094. return callback(null, result);
  1095. })
  1096. .catch(function (e) {
  1097. console.log("error saving one review:" + JSON.stringify(e));
  1098. if (e.code === 11000 || e.code === 11001) {
  1099. models.review.findOne({ "reviewId": review.reviewId })
  1100. .then(function (result) {
  1101. return callback(null, result);
  1102. })
  1103. .catch(function (e) {
  1104. console.log("error finding one review:" + JSON.stringify(e));
  1105. return callback(e, null);
  1106. });
  1107. } else {
  1108. return callback(e, null);
  1109. }
  1110. //return callback(e, null);
  1111. });
  1112. }, function (err, results) {
  1113. //console.log("Error map series: " + JSON.stringify(err));
  1114. //console.log("Finished map series!!!");
  1115. console.log("Map Series finish review length: " + results.length);
  1116. resolve(reviews);
  1117. });
  1118. });
  1119. })
  1120. .then(function (reviews) {
  1121. resolve(app);
  1122. })
  1123. .catch(function (e) {
  1124. reject(e);
  1125. })
  1126. });
  1127. })
  1128. .then(saveAppData)
  1129. .then(res.json.bind(res))
  1130. .catch(next);
  1131. });
  1132. router.get('/getPermissions/:appId', function (req, res, next) {
  1133. const opts = Object.assign({ appId: req.params.appId }, req.query);
  1134. models.app.findOne({ "appId": opts.appId })
  1135. .then(function (result) {
  1136. //console.log(result);
  1137. return result;
  1138. })
  1139. .then(fetchPermissions(opts))
  1140. .then(saveAppData)
  1141. .then(res.json.bind(res));
  1142. });
  1143. router.get('/getAppicaptorResult/:appId', function (req, res, next) {
  1144. const opts = Object.assign({ appId: req.params.appId }, req.query);
  1145. models.app.findOne({ "appId": opts.appId })
  1146. .then(function (result) {
  1147. //console.log(result);
  1148. return result;
  1149. })
  1150. .then(function (app) {
  1151. var obj = JSON.parse(fs.readFileSync('./appicaptor/outputall.json', 'utf8'));
  1152. //var appicaptorObj = _(obj.report.app).findWhere({ appId: app.appId });
  1153. var appicaptorObj = _.filter(obj.report.app, function (appicaptorApp) {
  1154. return appicaptorApp.attr.appId.split(":")[0] == app.appId;
  1155. })[0];
  1156. app.appicaptor = appicaptorObj;
  1157. return app;
  1158. })
  1159. .then(saveAppData)
  1160. .then(res.json.bind(res));
  1161. });
  1162. router.use(errorHandler);
  1163. module.exports = router;