var shellwords = require('shellwords'); var cp = require('child_process'); var semver = require('semver'); var isWSL = require('is-wsl'); var path = require('path'); var url = require('url'); var os = require('os'); var fs = require('fs'); var net = require('net'); const BUFFER_SIZE = 1024; function clone(obj) { return JSON.parse(JSON.stringify(obj)); } module.exports.clone = clone; var escapeQuotes = function (str) { if (typeof str === 'string') { return str.replace(/(["$`\\])/g, '\\$1'); } else { return str; } }; var inArray = function (arr, val) { return arr.indexOf(val) !== -1; }; var notifySendFlags = { u: 'urgency', urgency: 'urgency', t: 'expire-time', time: 'expire-time', timeout: 'expire-time', e: 'expire-time', expire: 'expire-time', 'expire-time': 'expire-time', i: 'icon', icon: 'icon', c: 'category', category: 'category', subtitle: 'category', h: 'hint', hint: 'hint', a: 'app-name', 'app-name': 'app-name' }; module.exports.command = function (notifier, options, cb) { notifier = shellwords.escape(notifier); if (process.env.DEBUG && process.env.DEBUG.indexOf('notifier') !== -1) { console.info('node-notifier debug info (command):'); console.info('[notifier path]', notifier); console.info('[notifier options]', options.join(' ')); } return cp.exec(notifier + ' ' + options.join(' '), function ( error, stdout, stderr ) { if (error) return cb(error); cb(stderr, stdout); }); }; module.exports.fileCommand = function (notifier, options, cb) { if (process.env.DEBUG && process.env.DEBUG.indexOf('notifier') !== -1) { console.info('node-notifier debug info (fileCommand):'); console.info('[notifier path]', notifier); console.info('[notifier options]', options.join(' ')); } return cp.execFile(notifier, options, function (error, stdout, stderr) { if (error) return cb(error, stdout); cb(stderr, stdout); }); }; module.exports.fileCommandJson = function (notifier, options, cb) { if (process.env.DEBUG && process.env.DEBUG.indexOf('notifier') !== -1) { console.info('node-notifier debug info (fileCommandJson):'); console.info('[notifier path]', notifier); console.info('[notifier options]', options.join(' ')); } return cp.execFile(notifier, options, function (error, stdout, stderr) { if (error) return cb(error, stdout); if (!stdout) return cb(error, {}); try { var data = JSON.parse(stdout); cb(!stderr ? null : stderr, data); } catch (e) { cb(e, stdout); } }); }; module.exports.immediateFileCommand = function (notifier, options, cb) { if (process.env.DEBUG && process.env.DEBUG.indexOf('notifier') !== -1) { console.info('node-notifier debug info (notifier):'); console.info('[notifier path]', notifier); } notifierExists(notifier, function (_, exists) { if (!exists) { return cb(new Error('Notifier (' + notifier + ') not found on system.')); } cp.execFile(notifier, options); cb(); }); }; function notifierExists(notifier, cb) { return fs.stat(notifier, function (err, stat) { if (!err) return cb(err, stat.isFile()); // Check if Windows alias if (path.extname(notifier)) { // Has extentioon, no need to check more return cb(err, false); } // Check if there is an exe file in the directory return fs.stat(notifier + '.exe', function (err, stat) { if (err) return cb(err, false); cb(err, stat.isFile()); }); }); } var mapAppIcon = function (options) { if (options.appIcon) { options.icon = options.appIcon; delete options.appIcon; } return options; }; var mapText = function (options) { if (options.text) { options.message = options.text; delete options.text; } return options; }; var mapIconShorthand = function (options) { if (options.i) { options.icon = options.i; delete options.i; } return options; }; module.exports.mapToNotifySend = function (options) { options = mapAppIcon(options); options = mapText(options); if (options.timeout === false) { delete options.timeout; } if (options.wait === true) { options['expire-time'] = 5; // 5 seconds default time (multipled below) } for (var key in options) { if (key === 'message' || key === 'title') continue; if (options.hasOwnProperty(key) && notifySendFlags[key] !== key) { options[notifySendFlags[key]] = options[key]; delete options[key]; } } if (typeof options['expire-time'] === 'undefined') { options['expire-time'] = 10 * 1000; // 10 sec timeout by default } else if (typeof options['expire-time'] === 'number') { options['expire-time'] = options['expire-time'] * 1000; // notify send uses milliseconds } return options; }; module.exports.mapToGrowl = function (options) { options = mapAppIcon(options); options = mapIconShorthand(options); options = mapText(options); if (options.icon && !Buffer.isBuffer(options.icon)) { try { options.icon = fs.readFileSync(options.icon); } catch (ex) {} } return options; }; module.exports.mapToMac = function (options) { options = mapIconShorthand(options); options = mapText(options); if (options.icon) { options.appIcon = options.icon; delete options.icon; } if (options.sound === true) { options.sound = 'Bottle'; } if (options.sound === false) { delete options.sound; } if (options.sound && options.sound.indexOf('Notification.') === 0) { options.sound = 'Bottle'; } if (options.wait === true) { if (!options.timeout) { options.timeout = 5; } delete options.wait; } if (!options.wait && !options.timeout) { if (options.timeout === false) { delete options.timeout; } else { options.timeout = 10; } } options.json = true; return options; }; function isArray(arr) { return Object.prototype.toString.call(arr) === '[object Array]'; } module.exports.isArray = isArray; function noop() {} module.exports.actionJackerDecorator = function (emitter, options, fn, mapper) { options = clone(options); fn = fn || noop; if (typeof fn !== 'function') { throw new TypeError( 'The second argument must be a function callback. You have passed ' + typeof fn ); } return function (err, data) { var resultantData = data; var metadata = {}; // Allow for extra data if resultantData is an object if (resultantData && typeof resultantData === 'object') { metadata = resultantData; resultantData = resultantData.activationType; } // Sanitize the data if (resultantData) { resultantData = resultantData.toLowerCase().trim(); if (resultantData.match(/^activate|clicked$/)) { resultantData = 'activate'; } if (resultantData.match(/^timedout$/)) { resultantData = 'timeout'; } } fn.apply(emitter, [err, resultantData, metadata]); if (!mapper || !resultantData) return; var key = mapper(resultantData); if (!key) return; emitter.emit(key, emitter, options, metadata); }; }; module.exports.constructArgumentList = function (options, extra) { var args = []; extra = extra || {}; // Massive ugly setup. Default args var initial = extra.initial || []; var keyExtra = extra.keyExtra || ''; var allowedArguments = extra.allowedArguments || []; var noEscape = extra.noEscape !== undefined; var checkForAllowed = extra.allowedArguments !== undefined; var explicitTrue = !!extra.explicitTrue; var keepNewlines = !!extra.keepNewlines; var wrapper = extra.wrapper === undefined ? '"' : extra.wrapper; var escapeFn = function escapeFn(arg) { if (isArray(arg)) { return removeNewLines(arg.map(escapeFn).join(',')); } if (!noEscape) { arg = escapeQuotes(arg); } if (typeof arg === 'string' && !keepNewlines) { arg = removeNewLines(arg); } return wrapper + arg + wrapper; }; initial.forEach(function (val) { args.push(escapeFn(val)); }); for (var key in options) { if ( options.hasOwnProperty(key) && (!checkForAllowed || inArray(allowedArguments, key)) ) { if (explicitTrue && options[key] === true) { args.push('-' + keyExtra + key); } else if (explicitTrue && options[key] === false) continue; else args.push('-' + keyExtra + key, escapeFn(options[key])); } } return args; }; function removeNewLines(str) { var excapedNewline = process.platform === 'win32' ? '\\r\\n' : '\\n'; return str.replace(/\r?\n/g, excapedNewline); } /* ---- Options ---- [-t]