utils.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. var cp = require('child_process'),
  2. os = require('os'),
  3. fs = require('fs'),
  4. url = require('url'),
  5. path = require('path'),
  6. shellwords = require('shellwords'),
  7. semver = require('semver'),
  8. clone = require('lodash.clonedeep');
  9. var escapeQuotes = function (str) {
  10. if (typeof str === 'string') {
  11. return str.replace(/(["$`\\])/g, '\\$1');
  12. } else {
  13. return str;
  14. }
  15. };
  16. var inArray = function (arr, val) {
  17. for(var i = 0; i < arr.length; i++) {
  18. if (arr[i] === val) {
  19. return true;
  20. }
  21. }
  22. return false;
  23. };
  24. var notifySendFlags = {
  25. "u": "urgency",
  26. "urgency": "urgency",
  27. "t": "expire-time",
  28. "e": "expire-time",
  29. "expire": "expire-time",
  30. "expire-time": "expire-time",
  31. "i": "icon",
  32. "icon": "icon",
  33. "c": "category",
  34. "category": "category",
  35. "subtitle": "category",
  36. "h": "hint",
  37. "hint": "hint"
  38. };
  39. module.exports.command = function (notifier, options, cb) {
  40. notifier = shellwords.escape(notifier);
  41. return cp.exec(notifier + ' ' + options.join(' '), function (error, stdout, stderr) {
  42. if (error) return cb(error);
  43. cb(stderr, stdout);
  44. });
  45. };
  46. module.exports.fileCommand = function (notifier, options, cb) {
  47. return cp.execFile(notifier, options, function (error, stdout, stderr) {
  48. if (error) return cb(error, stdout);
  49. cb(stderr, stdout);
  50. });
  51. };
  52. module.exports.immediateFileCommand = function (notifier, options, cb) {
  53. notifierExists(notifier, function (exists) {
  54. if (!exists) return cb(new Error('Notifier (' + notifier + ') not found on system.'));
  55. cp.execFile(notifier, options);
  56. cb();
  57. });
  58. };
  59. function notifierExists (notifier, cb) {
  60. return fs.stat(notifier, function (err, stat) {
  61. if (!err) return cb(stat.isFile());
  62. // Check if Windows alias
  63. if (!!path.extname(notifier)) {
  64. // Has extentioon, no need to check more
  65. return cb(false);
  66. }
  67. // Check if there is an exe file in the directory
  68. return fs.stat(notifier + '.exe', function (err, stat) {
  69. cb(stat.isFile());
  70. });
  71. });
  72. }
  73. var mapAppIcon = function (options) {
  74. if (options.appIcon) {
  75. options.icon = options.appIcon;
  76. delete options.appIcon;
  77. }
  78. return options;
  79. };
  80. var mapText = function (options) {
  81. if (options.text) {
  82. options.message = options.text;
  83. delete options.text;
  84. }
  85. return options;
  86. };
  87. var mapIconShorthand = function (options) {
  88. if (options.i) {
  89. options.icon = options.i;
  90. delete options.i;
  91. }
  92. return options;
  93. };
  94. module.exports.mapToNotifySend = function (options) {
  95. options = mapAppIcon(options);
  96. options = mapText(options);
  97. for (var key in options) {
  98. if (key === "message" || key === "title") continue;
  99. if (options.hasOwnProperty(key) && (notifySendFlags[key] != key)) {
  100. options[notifySendFlags[key]] = options[key];
  101. delete options[key];
  102. }
  103. }
  104. return options;
  105. };
  106. module.exports.mapToGrowl = function (options) {
  107. options = mapAppIcon(options);
  108. options = mapIconShorthand(options);
  109. options = mapText(options);
  110. if (options.icon && !Buffer.isBuffer(options.icon)) {
  111. try {
  112. options.icon = fs.readFileSync(options.icon);
  113. }catch(ex){
  114. }
  115. }
  116. return options;
  117. };
  118. module.exports.mapToMac = function (options) {
  119. options = mapIconShorthand(options);
  120. options = mapText(options);
  121. if (options.icon) {
  122. options.appIcon = options.icon;
  123. delete options.icon;
  124. }
  125. if (options.sound === true) {
  126. options.sound = 'Bottle';
  127. }
  128. if (options.sound === false) {
  129. delete options.sound;
  130. }
  131. return options;
  132. };
  133. module.exports.actionJackerDecorator = function (emitter, options, fn, mapper) {
  134. options = clone(options);
  135. fn = fn || function (err, data) {};
  136. return function (err, data) {
  137. var resultantData = data;
  138. // Sanitize the data
  139. if(resultantData) {
  140. resultantData = resultantData.toLowerCase().trim();
  141. if(resultantData.match(/^activate/)) {
  142. resultantData = 'activate';
  143. }
  144. }
  145. fn.apply(emitter, [err, resultantData]);
  146. if (err || !mapper || !resultantData) return;
  147. var key = mapper(resultantData);
  148. if (!key) return;
  149. emitter.emit(key, emitter, options);
  150. };
  151. };
  152. module.exports.constructArgumentList = function (options, extra) {
  153. var args = [];
  154. extra = extra || {};
  155. // Massive ugly setup. Default args
  156. var initial = extra.initial || [];
  157. var keyExtra = extra.keyExtra || "";
  158. var allowedArguments = extra.allowedArguments || [];
  159. var noEscape = extra.noEscape !== void 0;
  160. var checkForAllowed = extra.allowedArguments !== void 0;
  161. var explicitTrue = !!extra.explicitTrue;
  162. var wrapper = extra.wrapper === void 0 ? '"' : extra.wrapper;
  163. var escapeFn = function(arg) {
  164. if (!noEscape) {
  165. arg = escapeQuotes(arg);
  166. }
  167. if(typeof arg === 'string'){
  168. arg = arg.replace(/\r?\n/g, '\\n');
  169. }
  170. return arg;
  171. }
  172. initial.forEach(function (val) {
  173. args.push(wrapper + escapeFn(val) + wrapper);
  174. });
  175. for(var key in options) {
  176. if (options.hasOwnProperty(key) && (!checkForAllowed || inArray(allowedArguments, key))) {
  177. if (explicitTrue && options[key] === true) args.push('-' + keyExtra + key);
  178. else if (explicitTrue && options[key] === false) continue;
  179. else args.push('-' + keyExtra + key, wrapper + escapeFn(options[key]) + wrapper);
  180. }
  181. }
  182. return args;
  183. };
  184. module.exports.mapToWin8 = function (options){
  185. options = mapAppIcon(options);
  186. options = mapText(options);
  187. if(options.icon){
  188. if (/^file:\/+/.test(options.icon)) {
  189. // should parse file protocol URL to path
  190. options.p = url.parse(options.icon).pathname.replace(/^\/(\w\:\/)/, "$1").replace(/\//g, "\\");
  191. } else {
  192. options.p = options.icon;
  193. }
  194. delete options.icon;
  195. }
  196. if(options.message){
  197. // Remove escape char to debug "HRESULT : 0xC00CE508" exception
  198. options.m = options.message.replace(/\x1b/g, '');
  199. delete options.message;
  200. }
  201. if (options.title) {
  202. options.t = options.title;
  203. delete options.title;
  204. }
  205. if (options.quiet || options.silent) {
  206. options.q = options.quiet || options.silent;
  207. delete options.quiet;
  208. delete options.silent;
  209. }
  210. if (options.q !== false) {
  211. options.q = true;
  212. } else {
  213. delete options.q;
  214. }
  215. if (options.sound) {
  216. delete options.q;
  217. delete options.sound;
  218. }
  219. if (options.wait) {
  220. options.w = options.wait;
  221. delete options.wait;
  222. }
  223. return options;
  224. };
  225. module.exports.mapToNotifu = function (options) {
  226. options = mapAppIcon(options);
  227. options = mapText(options);
  228. if(options.icon){
  229. options.i = options.icon;
  230. delete options.icon;
  231. }
  232. if(options.message){
  233. options.m = options.message;
  234. delete options.message;
  235. }
  236. if (options.title) {
  237. options.p = options.title;
  238. delete options.title;
  239. }
  240. if (options.time) {
  241. options.d = options.time;
  242. delete options.time;
  243. }
  244. if (options.q !== false) {
  245. options.q = true;
  246. } else {
  247. delete options.q;
  248. }
  249. if (options.quiet === false) {
  250. delete options.q;
  251. delete options.quiet;
  252. }
  253. if (options.sound) {
  254. delete options.q;
  255. delete options.sound;
  256. }
  257. if (options.t) {
  258. options.d = options.t;
  259. delete options.t;
  260. }
  261. if (options.type) {
  262. options.t = sanitizeNotifuTypeArgument(options.type);
  263. delete options.type;
  264. }
  265. return options;
  266. };
  267. module.exports.isMac = function() {
  268. return os.type() === 'Darwin';
  269. };
  270. module.exports.isMountainLion = function() {
  271. return os.type() === 'Darwin' && semver.satisfies(garanteeSemverFormat(os.release()), '>=12.0.0');
  272. };
  273. module.exports.isWin8 = function() {
  274. return os.type() === 'Windows_NT' && semver.satisfies(garanteeSemverFormat(os.release()), '>=6.2.9200');
  275. };
  276. module.exports.isLessThanWin8 = function() {
  277. return os.type() === 'Windows_NT' && semver.satisfies(garanteeSemverFormat(os.release()), '<6.2.9200');
  278. };
  279. function garanteeSemverFormat (version) {
  280. if (version.split('.').length === 2) {
  281. version += '.0';
  282. }
  283. return version;
  284. }
  285. function sanitizeNotifuTypeArgument(type) {
  286. if (typeof type === 'string' || type instanceof String) {
  287. if (type.toLowerCase() == 'info')
  288. return 'info';
  289. if (type.toLowerCase() == 'warn')
  290. return 'warn';
  291. if (type.toLowerCase() == 'error')
  292. return 'error';
  293. }
  294. return 'info';
  295. }