index.js 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265
  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. var perPage = 10;
  528. var page = 0;
  529. if(req.query.genre) {
  530. filter.genreId = { $regex: '.*' + req.query.genre + '.*' };
  531. }
  532. if(req.query.page) {
  533. page = req.query.page;
  534. }
  535. models.app.find(filter).sort('title').skip(perPage * page).limit(20)
  536. .then((apps) => apps.map(function (app) {
  537. app.url = "http://localhost:3000/crawler/downloadedapps/" + app.appId;
  538. return app;
  539. }))
  540. .then(res.json.bind(res))
  541. .catch(function (e) {
  542. console.log("error fetching downloaded apps:" + JSON.stringify(e));
  543. next();
  544. });
  545. });
  546. router.get('/downloadedapps/:appId', function (req, res, next) {
  547. const opts = Object.assign({ appId: req.params.appId }, req.query);
  548. const posture = parseInt(req.query.posture || '0');
  549. var filter = {
  550. "appId": opts.appId
  551. };
  552. models.app.findOne(filter)
  553. .then(function (result) {
  554. return result.toObject();
  555. })
  556. .then(function (app) {
  557. return new Promise(function (resolve, reject) {
  558. models.review.aggregate([
  559. {
  560. $match: {
  561. app: app._id
  562. }
  563. },
  564. {
  565. $group: {
  566. _id: "$app",
  567. total_reviews_confidence: { $sum: "$sentiment.sentimentConfidence" },
  568. review_rating_sum: { $sum: "$sentiment.scaledReviewRating" },
  569. total_reviews: { $sum: 1 },
  570. total_ratingValue: { $sum: "$score" }
  571. }
  572. }],
  573. function (err, res) {
  574. if (err) {
  575. console.log("err: " + JSON.stringify(err));
  576. reject(err);
  577. }
  578. console.log("reviews total: " + JSON.stringify(res[0]));
  579. console.log("average: " + res[0].total_reviews_confidence / res[0].total_reviews);
  580. //source: http://stackoverflow.com/questions/929103/convert-a-number-range-to-another-range-maintaining-ratio
  581. //(((OldValue - OldMin) * (NewMax - NewMin)) / (OldMax - OldMin)) + NewMin
  582. app.reviewsTrustValue = ((((res[0].total_ratingValue / res[0].total_reviews) - 1) * (1 - 0)) / (5 - 1)) + 0;
  583. app.reviewsConfidenceValue = res[0].total_reviews_confidence / res[0].total_reviews;//averageReviewConfidence
  584. app.reviewMetric = res[0].review_rating_sum / res[0].total_reviews;
  585. resolve(app);
  586. }
  587. );
  588. });
  589. })
  590. .then(function (app) {
  591. return new Promise(function (resolve, reject) {
  592. models.app.aggregate([
  593. {
  594. $project: {
  595. genreId: 1,
  596. permissions_count: { $size: "$permissions" }
  597. }
  598. },
  599. {
  600. $match: {
  601. genreId: app.genreId
  602. }
  603. },
  604. {
  605. $group: {
  606. _id: "$genreId",
  607. same_genre_permissions_count: { $sum: "$permissions_count" },
  608. same_genre_count: { $sum: 1 }
  609. }
  610. }],
  611. function (err, res) {
  612. if (err) {
  613. console.log("err: " + JSON.stringify(err));
  614. reject(err);
  615. }
  616. //console.log("permissions total: " + JSON.stringify(res));
  617. app.categoryAveragePermission = Math.floor(res[0].same_genre_permissions_count / res[0].same_genre_count);
  618. console.log("categoryAveragePermission: " + app.categoryAveragePermission);
  619. let powerValue = app.permissions.length - app.categoryAveragePermission;
  620. app.permissionsTrustValue = (((app.score - 1) * (1 - 0)) / (5 - 1)) + 0;
  621. app.cofidenceInNumberofPermissions = (factorial(app.categoryAveragePermission) / factorial(app.permissions.length)) * Math.pow(app.categoryAveragePermission, powerValue);
  622. resolve(app);
  623. }
  624. );
  625. });
  626. })
  627. .then(function (app) {
  628. return new Promise(function (resolve, reject) {
  629. models.app.find({ "genreId": app.genreId })
  630. .then(function (results) {
  631. app.genreCount = results.length;
  632. resolve(app);
  633. })
  634. .catch(function (err) {
  635. reject(err);
  636. });
  637. });
  638. })
  639. .then(function (app) {
  640. console.log(app.histogram["1"]);
  641. let totalRatingsSum = (app.histogram["1"] * 1) + (app.histogram["2"] * 2) + (app.histogram["3"] * 3) + (app.histogram["4"] * 1) + (app.histogram["5"] * 5);
  642. let averageRating = totalRatingsSum / app.reviewsCount;
  643. console.log("rc: " + app.reviewsCount);
  644. console.log("av: " + averageRating);
  645. console.log("score: " + app.score);
  646. let wmd = 0;
  647. if (app.histogram["1"] + app.histogram["2"] + app.histogram["3"] == 0) {
  648. wmd = ((app.histogram["4"] * Math.abs(averageRating - 4)) / app.reviewsCount) + ((app.histogram["5"] * Math.abs(averageRating - 5)) / app.reviewsCount);
  649. } else if (app.histogram["3"] + app.histogram["4"] + app.histogram["5"] == 0) {
  650. wmd = ((app.histogram["1"] * Math.abs(averageRating - 1)) / app.reviewsCount) + ((app.histogram["2"] * Math.abs(averageRating - 2)) / app.reviewsCount);
  651. } else {
  652. let lowerRatingCount = app.histogram["1"] + app.histogram["2"] + app.histogram["3"];
  653. let upperRatingCount = app.histogram["5"] + app.histogram["4"] + app.histogram["3"];
  654. let lowerAverageRating = ((app.histogram["1"] * 1) + (app.histogram["2"] * 2) + (app.histogram["3"] * 3)) / lowerRatingCount;
  655. console.log("lowerAverageRating: " + lowerAverageRating);
  656. let upperAverageRating = ((app.histogram["5"] * 5) + (app.histogram["4"] * 4) + (app.histogram["3"] * 3)) / upperRatingCount;
  657. console.log("upperAverageRating: " + upperAverageRating);
  658. let lowerWeight = lowerRatingCount / (lowerRatingCount + upperRatingCount);
  659. console.log("lowerWeight: " + lowerWeight);
  660. let upperWeight = upperRatingCount / (lowerRatingCount + upperRatingCount);
  661. console.log("upperWeight: " + upperWeight);
  662. wmd = (lowerWeight * Math.abs(averageRating - lowerAverageRating)) + (upperWeight * Math.abs(averageRating - upperAverageRating));
  663. console.log("wmd: " + wmd);
  664. }
  665. app.averageRatingTrustValue = (((app.score - 1) * (1 - 0)) / (5 - 1)) + 0;
  666. app.averageRatingConfidenceValue = 1 - (wmd / 2);
  667. console.log("averageRatingConfidenceValue: " + app.averageRatingConfidenceValue);
  668. return app;
  669. })
  670. .then(function(app) {
  671. let sLength = 25;
  672. let pLength = 9;
  673. let sPositiveEvidences = 0;
  674. let sNegativeEvidences = 0;
  675. let pPositiveEvidences = 0;
  676. let pNegativeEvidences = 0;
  677. if(posture) {
  678. sLength--;
  679. pLength--;
  680. }
  681. _(app.appicaptor.indicator).each(function (element, index) {
  682. //console.log(element.attr);
  683. //console.log(index);//just the index 0....n
  684. if (element.attr.text == "Client communication used?") {
  685. if (element.attr.value == "yes") {
  686. sNegativeEvidences++;
  687. } else {
  688. sPositiveEvidences++;
  689. }
  690. }
  691. if (element.attr.text == "SSL/TLS used?") {
  692. if (element.attr.value == "no") {
  693. sNegativeEvidences++;
  694. } else {
  695. sPositiveEvidences++;
  696. }
  697. }
  698. if (element.attr.text == "Domains accessed with http AND https: ") {
  699. if (element.resultList[0].result.length > 0) {
  700. sNegativeEvidences++;
  701. } else {
  702. sPositiveEvidences++;
  703. }
  704. }
  705. if (element.attr.text == "Custom SSL/TLS trust manager implemented?") {
  706. if (element.attr.value == "yes") {
  707. sNegativeEvidences++;
  708. } else {
  709. sPositiveEvidences++;
  710. }
  711. }
  712. if (element.attr.text == "Faulty custom SSL/TLS trust manager implemented?") {
  713. if (element.attr.value == "yes") {
  714. sNegativeEvidences++;
  715. } else {
  716. sPositiveEvidences++;
  717. }
  718. }
  719. if (element.attr.text == "SSL/TLS using custom error handling?") {
  720. if (element.attr.value == "yes") {
  721. sNegativeEvidences++;
  722. } else {
  723. sPositiveEvidences++;
  724. }
  725. }
  726. if (element.attr.text == "SSL/TLS using faulty custom error handling?") {
  727. if (element.attr.value == "yes") {
  728. sNegativeEvidences++;
  729. } else {
  730. sPositiveEvidences++;
  731. }
  732. }
  733. if (element.attr.text == "SSL/TLS using manual domain name verification?") {
  734. if (element.attr.value == "yes") {
  735. sNegativeEvidences++;
  736. } else {
  737. sPositiveEvidences++;
  738. }
  739. }
  740. if (element.attr.text == "Unprotected communication?") {
  741. if (element.attr.value == "yes") {
  742. sNegativeEvidences++;
  743. } else {
  744. sPositiveEvidences++;
  745. }
  746. }
  747. if (element.attr.text == "Unprotected HTML?") {
  748. if (element.attr.value == "yes") {
  749. sNegativeEvidences++;
  750. } else {
  751. sPositiveEvidences++;
  752. }
  753. }
  754. if (element.attr.text == "Cryptographic Primitives: ") {
  755. if (element.resultList[0].result.length > 0) {
  756. sNegativeEvidences++;
  757. } else {
  758. sPositiveEvidences++;
  759. }
  760. }
  761. if (element.attr.text == "Application needs dangerous permissions? ") {
  762. if (element.attr.value == "yes") {
  763. sNegativeEvidences++;
  764. } else {
  765. sPositiveEvidences++;
  766. }
  767. }
  768. if (element.attr.text == "JavaScript to SDK API bridge usage?") {
  769. if (element.attr.value == "yes") {
  770. sNegativeEvidences++;
  771. } else {
  772. sPositiveEvidences++;
  773. }
  774. }
  775. if (element.attr.text == "Is application overprivileged?" && !posture) {
  776. if (element.attr.value == "yes") {
  777. sNegativeEvidences++;
  778. } else {
  779. sPositiveEvidences++;
  780. }
  781. }
  782. if (element.attr.text == "Userdefined permission usage: ") {
  783. if (element.resultList[0].result.length > 0) {
  784. sNegativeEvidences++;
  785. } else {
  786. sPositiveEvidences++;
  787. }
  788. }
  789. if (element.attr.text == "WiFi-Direct enabled?") {
  790. if (element.attr.value == "yes") {
  791. sNegativeEvidences++;
  792. } else {
  793. sPositiveEvidences++;
  794. }
  795. }
  796. if (element.attr.text == "App can handle documents of mimeType: ") {
  797. if (element.attr.value == "none") {
  798. sPositiveEvidences++;
  799. } else {
  800. sNegativeEvidences++;
  801. }
  802. }
  803. if (element.attr.text == "Screenshot protection used?") {
  804. if (element.attr.value == "yes") {
  805. sPositiveEvidences++;
  806. } else {
  807. sNegativeEvidences++;
  808. }
  809. }
  810. if (element.attr.text == "Tap Jacking Protection used?") {
  811. if (element.attr.value == "yes") {
  812. sPositiveEvidences++;
  813. } else {
  814. sNegativeEvidences++;
  815. }
  816. }
  817. if (element.attr.text == "Scheduled Alarm Manager registered?") {
  818. if (element.attr.value == "no") {
  819. sPositiveEvidences++;
  820. } else {
  821. sNegativeEvidences++;
  822. }
  823. }
  824. if (element.attr.text == "Dynamically loaded code at runtime?") {
  825. if (element.attr.value == "no") {
  826. sPositiveEvidences++;
  827. } else {
  828. sNegativeEvidences++;
  829. }
  830. }
  831. if (element.attr.text == "Allow app debugging Flag?") {
  832. if (element.attr.value == "no") {
  833. sPositiveEvidences++;
  834. } else {
  835. sNegativeEvidences++;
  836. }
  837. }
  838. if (element.attr.text == "Allow autoexecute after Phone Reboot?") {
  839. if (element.attr.value == "no") {
  840. sPositiveEvidences++;
  841. } else {
  842. sNegativeEvidences++;
  843. }
  844. }
  845. if (element.attr.text == "App uses outdated signature key?") {
  846. if (element.attr.value == "no") {
  847. sPositiveEvidences++;
  848. } else {
  849. sNegativeEvidences++;
  850. }
  851. }
  852. if (element.attr.text == "Contains native libraries: ") {
  853. if (element.attr.value == "no") {
  854. sPositiveEvidences++;
  855. } else {
  856. sNegativeEvidences++;
  857. }
  858. }
  859. //privacy
  860. if (element.attr.text == "Obfuscation used?") {
  861. if (element.attr.value == "yes") {
  862. pPositiveEvidences++;
  863. } else {
  864. pNegativeEvidences++;
  865. }
  866. }
  867. if (element.attr.text == "Device administration policy entries: ") {
  868. if (element.attr.value == "none") {
  869. pPositiveEvidences++;
  870. } else {
  871. pNegativeEvidences++;
  872. }
  873. }
  874. if (element.attr.text == "Accessed unique identifier(s): ") {
  875. if (element.resultList[0].result.length > 0) {
  876. pNegativeEvidences++;
  877. } else {
  878. pPositiveEvidences++;
  879. }
  880. }
  881. if (element.attr.text == "Advertisment-/tracking frameworks found: " && !posture) {
  882. if (element.attr.value == "none") {
  883. pPositiveEvidences++;
  884. } else if (element.resultList[0].result.length > 10) {
  885. pNegativeEvidences++;
  886. } else {
  887. pPositiveEvidences++;
  888. }
  889. }
  890. if (element.attr.text == "App provides public accessible activities?") {
  891. if (element.attr.value == "yes") {
  892. pNegativeEvidences++;
  893. } else {
  894. pPositiveEvidences++;
  895. }
  896. }
  897. if (element.attr.text == "Backup of app is allowed?") {
  898. if (element.attr.value == "yes") {
  899. pNegativeEvidences++;
  900. } else {
  901. pPositiveEvidences++;
  902. }
  903. }
  904. if (element.attr.text == "Log Statement Enabled?") {
  905. if (element.attr.value == "yes") {
  906. pNegativeEvidences++;
  907. } else {
  908. pPositiveEvidences++;
  909. }
  910. }
  911. if (element.attr.text == "Permission to access address book?") {
  912. if (element.attr.value == "yes") {
  913. pNegativeEvidences++;
  914. } else {
  915. pPositiveEvidences++;
  916. }
  917. }
  918. if (element.attr.text == "Unprotected preference files found?") {
  919. if (element.attr.value == "yes") {
  920. pNegativeEvidences++;
  921. } else {
  922. pPositiveEvidences++;
  923. }
  924. }
  925. })
  926. console.log("sPositiveEvidences " + sPositiveEvidences);
  927. console.log("sNegativeEvidences " + sNegativeEvidences);
  928. console.log("pPositiveEvidences " + pPositiveEvidences);
  929. console.log("pNegativeEvidences " + pNegativeEvidences);
  930. app.sTrustValue = sPositiveEvidences/(sPositiveEvidences+sNegativeEvidences);
  931. app.sConfidenceValue = (sLength*(sPositiveEvidences+sNegativeEvidences))/((2*(sLength-(sPositiveEvidences+sNegativeEvidences)))+(sLength*(sPositiveEvidences+sNegativeEvidences)));
  932. app.pTrustValue = pPositiveEvidences/(pPositiveEvidences+pNegativeEvidences);
  933. app.pConfidenceValue = (pLength*(pPositiveEvidences+pNegativeEvidences))/((2*(pLength-(pPositiveEvidences+pNegativeEvidences)))+(pLength*(pPositiveEvidences+pNegativeEvidences)));
  934. return app;
  935. })
  936. .then(res.json.bind(res))
  937. .catch(function (e) {
  938. console.log("error fetching downloaded app: " + JSON.stringify(e));
  939. next();
  940. });
  941. });
  942. function doRequest(opts) { //will be used in later phase may be
  943. return new Promise((resolve, reject) => requestLib(opts, function (error, response, body) {
  944. if (error) {
  945. return reject(error);
  946. }
  947. if (response.statusCode >= 400) {
  948. return reject({ response });
  949. }
  950. resolve(body);
  951. }));
  952. }
  953. router.get('/insertAppicaptorData/:appId', function (req, res, next) {
  954. var obj = JSON.parse(fs.readFileSync('./appicaptor/outputall.json', 'utf8'));
  955. var outputObj = {
  956. overprivilegedPermission: {},
  957. dangerousPermission: {},
  958. extensiveTrackingFrameworkUsage: {},
  959. outdatedCryptographicPrimitives: {},
  960. unprotectedCommunication: {}
  961. };
  962. for (var i = 0; i < obj.report.app.length; i++) {
  963. //console.log("ff: " + obj.report.app[i].$.appId.split(":")[0]);
  964. //console.log("ll: " + req.params.appId);
  965. if (obj.report.app[i].attr.appId.split(":")[0] == req.params.appId) {
  966. //console.log("isnersfsdf");
  967. for (var j = 0; j < obj.report.app[i].indicator.length; j++) {
  968. //do something with obj[i]
  969. for (var ind in obj.report.app[i].indicator[j]) {
  970. for (var vals in obj.report.app[i].indicator[j][ind]) {
  971. //Privacy Risks
  972. if (obj.report.app[i].indicator[j][ind][vals] == "Redundant permission correlation") {
  973. outputObj.overprivilegedPermission.Value = obj.report.app[i].indicator[j][ind]["value"];
  974. }
  975. if (obj.report.app[i].indicator[j][ind][vals] == "Overprivileged permission yes/no") {
  976. if (obj.report.app[i].indicator[j]["resultList"] != undefined) {
  977. outputObj.overprivilegedPermission.List = obj.report.app[i].indicator[j]["resultList"][0].result;
  978. }
  979. }
  980. if (obj.report.app[i].indicator[j][ind][vals] == "Dangerous Permission") {
  981. outputObj.dangerousPermission.Value = obj.report.app[i].indicator[j][ind]["value"];
  982. if (obj.report.app[i].indicator[j]["detailList"] != undefined) {
  983. outputObj.dangerousPermission.List = obj.report.app[i].indicator[j]["detailList"][0].detail;
  984. }
  985. }
  986. if (obj.report.app[i].indicator[j][ind][vals] == "privacy-risk") {
  987. outputObj.extensiveTrackingFrameworkUsage.Value = obj.report.app[i].indicator[j][ind]["value"];
  988. if (obj.report.app[i].indicator[j]["detailList"] != undefined) {
  989. outputObj.extensiveTrackingFrameworkUsage.Reason = obj.report.app[i].indicator[j]["detailList"][0].detail;
  990. }
  991. }
  992. if (obj.report.app[i].indicator[j][ind][vals] == "Advertisment/Tracking Usage") {
  993. if (obj.report.app[i].indicator[j]["resultList"] != undefined) {
  994. outputObj.extensiveTrackingFrameworkUsage.List = obj.report.app[i].indicator[j]["resultList"][0].result;
  995. }
  996. }
  997. //Security Risks
  998. if (obj.report.app[i].indicator[j][ind][vals] == "Cryptographic Primitives: ") {
  999. outputObj.outdatedCryptographicPrimitives.Value = obj.report.app[i].indicator[j][ind]["value"];
  1000. if (obj.report.app[i].indicator[j]["resultList"] != undefined) {
  1001. outputObj.outdatedCryptographicPrimitives.List = obj.report.app[i].indicator[j]["resultList"][0].result;
  1002. }
  1003. }
  1004. if (obj.report.app[i].indicator[j][ind][vals] == "HTTP Access") {
  1005. outputObj.unprotectedCommunication.Value = obj.report.app[i].indicator[j][ind]["value"];
  1006. if (obj.report.app[i].indicator[j]["detailList"] != undefined) {
  1007. outputObj.unprotectedCommunication.List = obj.report.app[i].indicator[j]["detailList"][0].detail;
  1008. }
  1009. }
  1010. }
  1011. }
  1012. }
  1013. }
  1014. }
  1015. models.app.findOne({ "appId": req.params.appId })
  1016. .then(function (result) {
  1017. //console.log(outputObj);
  1018. result.appicaptor = outputObj;
  1019. return result;
  1020. })
  1021. .then(saveAppData)
  1022. .then(res.json.bind(res))
  1023. .catch(function (e) {
  1024. console.log("error fetching downloaded apps:" + JSON.stringify(e));
  1025. next();
  1026. });
  1027. });
  1028. router.get('/getDownloadInfo/:appId', function (req, res, next) {//if no data is avaiable it will first download app's data first
  1029. const opts = Object.assign({ appId: req.params.appId }, req.query);
  1030. models.app.findOne({ "appId": opts.appId })
  1031. .then(function (result) {
  1032. //console.log(result);
  1033. if (result) {
  1034. return result.toObject();
  1035. } else {
  1036. //return null;
  1037. return new Promise(function (resolve, reject) {
  1038. gplay.app(opts)
  1039. .then(cleanUrls(req))
  1040. .then(function (app) {
  1041. app.reviews = [];
  1042. app.permissions = [];
  1043. return new models.app(app);
  1044. })
  1045. .then(saveAppData)
  1046. .then(function(app) {
  1047. resolve(app);
  1048. })
  1049. .catch(function(e) {
  1050. reject(e);
  1051. });
  1052. });
  1053. }
  1054. })
  1055. .then(res.json.bind(res));
  1056. });
  1057. router.get('/getReviews/:appId', function (req, res, next) {
  1058. const opts = Object.assign({ appId: req.params.appId }, req.query);
  1059. models.app.findOne({ "appId": opts.appId })
  1060. .then(function (result) {
  1061. //console.log(result);
  1062. return result;
  1063. })
  1064. .then(fetchReviewAndSave(opts))
  1065. .then(saveAppData)
  1066. .then(res.json.bind(res));
  1067. });
  1068. //not used
  1069. router.get('/getReviewsFromGoogle/:appId', function (req, res, next) {
  1070. const opts = Object.assign({ appId: req.params.appId }, req.query);
  1071. opts.page = parseInt(req.query.page || '0');
  1072. gplay.reviews(opts)
  1073. .then(toList)
  1074. .then(res.json.bind(res))
  1075. .catch(next);
  1076. });
  1077. //not used
  1078. router.get('/fetchReview/:appId', function (req, res, next) {
  1079. const opts = Object.assign({ appId: req.params.appId }, req.query);
  1080. opts.page = parseInt(req.query.page || '0');
  1081. console.log("review page no:" + opts.page);
  1082. models.app.findOne({ "appId": opts.appId })
  1083. .then(function (result) {
  1084. return result;
  1085. })
  1086. .then(function (app) {
  1087. return new Promise(function (resolve, reject) {
  1088. gplay
  1089. .reviews(opts)
  1090. .then((reviews) => reviews.map(addSentiment()))
  1091. .then(function (reviews) {
  1092. console.log("Reviews length: " + reviews.length);
  1093. return new Promise(function (resolve, reject) {
  1094. async.mapSeries(reviews, function (review, callback) {
  1095. review.app = app._id;
  1096. new models.review(review).save()
  1097. .then(function (result) {
  1098. app.reviews.push(result._id);
  1099. return callback(null, result);
  1100. })
  1101. .catch(function (e) {
  1102. console.log("error saving one review:" + JSON.stringify(e));
  1103. if (e.code === 11000 || e.code === 11001) {
  1104. models.review.findOne({ "reviewId": review.reviewId })
  1105. .then(function (result) {
  1106. return callback(null, result);
  1107. })
  1108. .catch(function (e) {
  1109. console.log("error finding one review:" + JSON.stringify(e));
  1110. return callback(e, null);
  1111. });
  1112. } else {
  1113. return callback(e, null);
  1114. }
  1115. //return callback(e, null);
  1116. });
  1117. }, function (err, results) {
  1118. //console.log("Error map series: " + JSON.stringify(err));
  1119. //console.log("Finished map series!!!");
  1120. console.log("Map Series finish review length: " + results.length);
  1121. resolve(reviews);
  1122. });
  1123. });
  1124. })
  1125. .then(function (reviews) {
  1126. resolve(app);
  1127. })
  1128. .catch(function (e) {
  1129. reject(e);
  1130. })
  1131. });
  1132. })
  1133. .then(saveAppData)
  1134. .then(res.json.bind(res))
  1135. .catch(next);
  1136. });
  1137. router.get('/getPermissions/:appId', function (req, res, next) {
  1138. const opts = Object.assign({ appId: req.params.appId }, req.query);
  1139. models.app.findOne({ "appId": opts.appId })
  1140. .then(function (result) {
  1141. //console.log(result);
  1142. return result;
  1143. })
  1144. .then(fetchPermissions(opts))
  1145. .then(saveAppData)
  1146. .then(res.json.bind(res));
  1147. });
  1148. router.get('/getAppicaptorResult/:appId', function (req, res, next) {
  1149. const opts = Object.assign({ appId: req.params.appId }, req.query);
  1150. models.app.findOne({ "appId": opts.appId })
  1151. .then(function (result) {
  1152. //console.log(result);
  1153. return result;
  1154. })
  1155. .then(function (app) {
  1156. var obj = JSON.parse(fs.readFileSync('./appicaptor/outputall.json', 'utf8'));
  1157. //var appicaptorObj = _(obj.report.app).findWhere({ appId: app.appId });
  1158. var appicaptorObj = _.filter(obj.report.app, function (appicaptorApp) {
  1159. return appicaptorApp.attr.appId.split(":")[0] == app.appId;
  1160. })[0];
  1161. app.appicaptor = appicaptorObj;
  1162. return app;
  1163. })
  1164. .then(saveAppData)
  1165. .then(res.json.bind(res));
  1166. });
  1167. router.use(errorHandler);
  1168. module.exports = router;