index.js 42 KB

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