<commit_before>/*
 * benc2json
 * https://github.com/pwmckenna/benc2json
 *
 * Copyright (c) 2013 Patrick Williams
 * Licensed under the MIT license.
 */

var Firebase = require('./firebase-node');
var http = require('http');
var URL = require('url');
var q = require('q');
var request = require('request');
var URL = require('url');

var headers = {
    'Content-Type': 'application/json',
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'GET,PUT,POST,DELETE',
    'Access-Control-Allow-Headers': 'Content-Type'
};
var MAGNET_LINK_IDENTIFIER = 'magnet:?xt=urn:btih:';

var addFirebaseTracker = function(firebase, provider, id, info_hash, src, labels) {
    console.log('addFirebaseTracker', provider, id, info_hash, src);

    var userTracker = firebase.child('users').child(provider).child(id).child('trackers').push();
    var globalTracker = firebase.child('trackers').push();
    globalTracker.set({
        user: {
            id: id,
            provider: provider
        },
        info_hash: info_hash,
        stats: {
            completed: 0,
            transferred: 0,
        },
        src: src,
        labels: labels || [],
        time: new Date().getTime()
    });
    userTracker.set(globalTracker.name());
    return globalTracker;
}

var addFirebaseTorrent = function(firebase, provider, id, tracker, torrent) {
    var file = URL.parse(torrent).pathname;
    file = file.substring(file.lastIndexOf('/'));
    var shortened = firebase.child('torrent').push({
        tracker: tracker,
        torrent: torrent,
        user: {
            id: id,
            provider: provider
        }
    });
    return 'http://torrent.todium.com/' + shortened.name() + '.torrent';
}

var addLinkRequest = function(firebase, provider, id, src, labels) {
    console.log('addLinkRequest', provider, id, src);

    var ret = new q.defer();
    if(src.indexOf(MAGNET_LINK_IDENTIFIER) === 0) {
        console.log('replacing magnet link');
        var info_hash = magnetLink.substr(MAGNET_LINK_IDENTIFIER.length, 40);
        var tracker = addFirebaseTracker(firebase, provider, id, info_hash, src, labels);
        var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
        var trackable = src + '&tr=' + url;
        tracker.child('trackable').set(trackable);
        ret.resolve(trackable);
    } else {
        console.log('replacing torrent file');
        request.get({
            url: 'http://hasher.todium.com?torrent=' + src,
            json: true
        }, function (error, response, info_hash) {
            console.log(error, response.statusCode, info_hash);
            if(error || typeof info_hash !== 'string' || info_hash.length !== 40) {
                ret.reject(error);
                return;
            } else {
                var tracker = addFirebaseTracker(firebase, provider, id, info_hash, src, labels);
                var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
                var trackable = addFirebaseTorrent(firebase, provider, id, url, src);
                tracker.child('trackable').set(trackable);
                ret.resolve(trackable);
            }
        });
    }
    return ret.promise;
}

var authenticateFirebaseRequest = function(firebase, token) {
    var ret = new q.defer();
    firebase.auth(token, function(error, info) {
        console.log('firebase auth', error, info);
        if(error) {
            ret.reject(error);
        } else {
           ret.resolve(info);
        }
    }, function(error) {
        ret.reject(error);
    });
    return ret.promise;
}

var port = process.env.PORT || 5050;
console.log('port', port);
http.createServer(function(req, res) {
    var query = URL.parse(req.url, true).query;

    // if we don't have a url argument, then lets bail
    if(!query.hasOwnProperty('token') ||
        !query.hasOwnProperty('src')
    ) {
        res.writeHead(400);
        res.end('src/token mandatory');
        return;
    }

    var token = query['token'];
    var src = query['src'];
    var labels = query['labels'];

    console.log('token', token.length, token);
    console.log('src', src.length, src);

    var firebase = new Firebase('https://featuredcontent.firebaseio.com/');
    var authenticationRequest = authenticateFirebaseRequest(firebase, token);

    var abort = function(err) {
        res.writeHead(400);
        res.end(err);
    };

    authenticationRequest.then(function(info) {
        var provider = info.auth.provider;
        var id = info.auth.id;
        addLinkRequest(firebase, provider, id, src, labels).then(function(link) {
            res.writeHead(200, headers);
            if(query.hasOwnProperty('callback')) {
                var callback = query['callback'];
                res.end(callback + '("' + link + '")');
            } else {
                res.end(link);
            }
            firebase.unauth();
        }, abort);
    }, abort);
}).listen(port, function() {
    console.log('Listening on ' + port);
});<commit_msg>adapting to using campaigns ids and secrets, rather than user credentials<commit_after>/*
 * benc2json
 * https://github.com/pwmckenna/benc2json
 *
 * Copyright (c) 2013 Patrick Williams
 * Licensed under the MIT license.
 */

var Firebase = require('./firebase-node');
var http = require('http');
var URL = require('url');
var q = require('q');
var request = require('request');
var URL = require('url');

var headers = {
    'Content-Type': 'application/json',
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'GET,PUT,POST,DELETE',
    'Access-Control-Allow-Headers': 'Content-Type'
};
var MAGNET_LINK_IDENTIFIER = 'magnet:?xt=urn:btih:';
var firebase = new Firebase('https://todium.firebaseio.com/');

var addFirebaseTracker = function(firebase, id, info_hash, src) {
    console.log('addFirebaseTracker', id, info_hash, src);

    var campaignTracker = firebase.child('campaigns').child(id).child('trackers').push();
    var globalTracker = firebase.child('trackers').push();
    globalTracker.set({
        compaign: id,
        info_hash: info_hash,
        stats: {
            completed: 0,
            transferred: 0,
        },
        src: src,
        time: new Date().getTime()
    });
    campaignTracker.set(globalTracker.name());
    return globalTracker;
}

var addFirebaseTorrent = function(firebase, id, tracker, torrent) {
    var file = URL.parse(torrent).pathname;
    file = file.substring(file.lastIndexOf('/'));
    var shortened = firebase.child('torrent').push({
        tracker: tracker,
        torrent: torrent,
        campaign: id
    });
    return 'http://torrent.todium.com/' + shortened.name() + '.torrent';
}

var addLinkRequest = function(firebase, id, src) {
    console.log('addLinkRequest', id, src);

    var ret = new q.defer();
    if(src.indexOf(MAGNET_LINK_IDENTIFIER) === 0) {
        console.log('replacing magnet link');
        var info_hash = magnetLink.substr(MAGNET_LINK_IDENTIFIER.length, 40);
        var tracker = addFirebaseTracker(firebase, id, info_hash, src);
        var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
        var trackable = src + '&tr=' + url;
        tracker.child('trackable').set(trackable);
        ret.resolve(trackable);
    } else {
        console.log('replacing torrent file', src);
        request.get({
            url: 'http://hasher.todium.com',
            qs: {
                torrent: src
            },
            json: true
        }, function (error, response, info_hash) {
            console.log(error, response.statusCode, info_hash);
            if(error || typeof info_hash !== 'string' || info_hash.length !== 40) {
                ret.reject(error);
                return;
            } else {
                var tracker = addFirebaseTracker(firebase, id, info_hash, src);
                var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
                var trackable = addFirebaseTorrent(firebase, id, url, src);
                tracker.child('trackable').set(trackable);
                ret.resolve(trackable);
            }
        });
    }
    return ret.promise;
};

var authenticationRequest = function(id, secret) {
    console.log('authenticationRequest', id, secret);
    var ret = new q.defer();
    firebase.child('campaigns').child(id).once('value', function(campaignSnapshot) {
        var campaign = campaignSnapshot.val();
        console.log('campaign ' + id + ' value', campaign);
        if(!campaign) {
            var error = 'invalid campaign id';
            console.error(error);
            return ret.reject(error);
        }

        if(campaign.secret !== secret) {
            var error = 'invalid campaign secret';
            console.error(error);
            return ret.reject(error);
        }

        return ret.resolve();
    });
    return ret.promise;
};

var port = process.env.PORT || 5050;
console.log('port', port);
http.createServer(function(req, res) {
    var abort = function(err) {
        res.writeHead(400);
        res.end(err);
    };

    var query = URL.parse(req.url, true).query;

    // if we don't have a url argument, then lets bail
    if(!query.hasOwnProperty('id') ||
        !query.hasOwnProperty('secret') ||
        !query.hasOwnProperty('src')
    ) {
        abort('src/id/secret mandatory');
        return;
    }
    var id = query['id'];
    var secret = query['secret'];
    var src = query['src'];

    console.log('id', id);
    console.log('secret', secret);
    console.log('src', src);

    authenticationRequest(id, secret).then(function() {
        addLinkRequest(firebase, id, src).then(function(link) {
            res.writeHead(200, headers);
            if(query.hasOwnProperty('callback')) {
                var callback = query['callback'];
                res.end(callback + '("' + link + '")');
            } else {
                res.end(link);
            }
        }, abort);
    }, abort);
}).listen(port, function() {
    console.log('Listening on ' + port);
});<|endoftext|>


<commit_before>"use strict";

var cors = require("cors"),
    express = require("express");

module.exports = function(source) {
  var app = express();

  app.disable('x-powered-by');

  app.use(cors());

  app.get("/", function(req, res, next) {
    // TODO what happens when passthrough's buffer fills up?
    res.header("Content-Type", "text/plain");

    return source.pipe(res);
  });

  return app;
};
<commit_msg>Output compression<commit_after>"use strict";

var zlib = require("zlib");

var cors = require("cors"),
    express = require("express");

module.exports = function(source) {
  var app = express();

  app.disable('x-powered-by');

  app.use(cors());

  app.get("/", function(req, res, next) {
    // TODO what happens when passthrough's buffer fills up?
    res.header("Content-Type", "text/plain");

    // compression
    var acceptEncoding = req.headers["accept-encoding"] || "",
        options = {
          flush: zlib.Z_PARTIAL_FLUSH
        };

    if (acceptEncoding.indexOf("gzip") >= 0) {
      res.header("Content-Encoding", "gzip");
      return source.pipe(zlib.createGzip(options)).pipe(res);
    }

    if (acceptEncoding.indexOf("deflate") >= 0) {
      res.header("Content-Encoding", "deflate");
      return source.pipe(zlib.createDeflate(options)).pipe(res);
    }

    return source.pipe(res);
  });

  return app;
};
<|endoftext|>


<commit_before>'use strict';

var path = require('path'),
    q = require('q'),
    fs = require('q-io/fs'),
    temp = require('temp'),
    _ = require('lodash'),
    findGemini = require('./find-gemini'),

    Tests = require('./tests-model'),
    Index = require('./common/tests-index'),
    EventSource = require('./event-source'),
    reporter = require('./reporter');

function removeTreeIfExists(path) {
    return fs.exists(path)
        .then(function(exists) {
            if (exists) {
                return fs.removeTree(path);
            }
        });
}

function App(options) {
    this.diffDir = temp.path('gemini-gui-diff');
    this.currentDir = temp.path('gemini-gui-curr');
    this._eventSource = new EventSource();

    var Gemini = findGemini();
    this._gemini = new Gemini(options.configFile, {cli: true, env: true});

    var browserIds = this._gemini.browserIds;
    this.referenceDirs = _.zipObject(
        browserIds,
        browserIds.map(function(browserId) {
            return this._gemini.config.forBrowser(browserId).screenshotsDir;
        }.bind(this))
    );
}

App.prototype.addClient = function(connection) {
    this._eventSource.addConnection(connection);
};

App.prototype.sendClientEvent = function(event, data) {
    this._eventSource.emit(event, data);
};

App.prototype.readTests = function(paths, grep) {
    var _this = this;
    if (!this._tests) {
        return this._gemini.readTests(paths, grep)
            .then(function(suiteCollection) {
                _this._tests = new Tests(_this, suiteCollection);
                return _this._tests.data;
            });
    }
    return q.resolve(this._tests.data);
};

App.prototype.run = function(paths, grep) {
    var _this = this;
    this._failedTests = new Index();
    return this._recreateTmpDirs()
        .then(function() {
            return _this._gemini.test(paths, {
                reporters: [reporter(_this), 'flat'],
                tempDir: _this.currentDir,
                grep: grep
            });
        });
};

App.prototype._recreateTmpDirs = function() {
    var _this = this;
    return q.all([removeTreeIfExists(this.currentDir), removeTreeIfExists(this.diffDir)])
        .then(function() {
            return q.all([
                fs.makeDirectory(_this.currentDir),
                fs.makeDirectory(_this.diffDir)
            ]);
        });
};

App.prototype.buildDiff = function(failureReport) {
    var _this = this;
    return this._buildDiffFile(failureReport)
        .then(function(diffPath) {
            return _this.diffPathToURL(diffPath);
        });
};

App.prototype._buildDiffFile = function(failureReport) {
    var diffPath = temp.path({dir: this.diffDir, suffix: '.png'});
    return failureReport.saveDiffTo(diffPath).thenResolve(diffPath);
};

App.prototype.addFailedTest = function(test) {
    this._failedTests.add(test);
};

App.prototype.updateReferenceImage = function(testData) {
    var _this = this,
        test = this._failedTests.find(testData);
    if (!test) {
        return q.reject(new Error('No such test failed'));
    }
    return fs.copy(test.currentPath, test.referencePath)
        .then(function() {
            // add query string timestamp to avoid caching on client
            return _this.refPathToURL(test.referencePath, test.browserId) + '?t=' + new Date().valueOf();
        });
};

App.prototype.getScreenshotPath = function(suite, stateName, browserId) {
    return this._gemini.getScreenshotPath(suite, stateName, browserId);
};

App.prototype.getBrowserCapabilites = function(browserId) {
    return this._gemini.getBrowserCapabilites(browserId);
};

App.prototype.refPathToURL = function(fullPath, browserId) {
    return this._pathToURL(this.referenceDirs[browserId], fullPath, App.refPrefix + '/' + browserId);
};

App.prototype.currentPathToURL = function(fullPath) {
    return this._pathToURL(this.currentDir, fullPath, App.currentPrefix);
};

App.prototype.diffPathToURL = function(fullPath) {
    return this._pathToURL(this.diffDir, fullPath, App.diffPrefix);
};

App.prototype._pathToURL = function(rootDir, fullPath, prefix) {
    var relPath = path.relative(rootDir, fullPath);
    return prefix + '/' + encodeURI(relPath);
};

App.refPrefix = '/ref';
App.currentPrefix = '/curr';
App.diffPrefix = '/diff';

module.exports = App;
<commit_msg>Logging updated reference image path<commit_after>'use strict';

var path = require('path'),
    q = require('q'),
    fs = require('q-io/fs'),
    temp = require('temp'),
    _ = require('lodash'),
    findGemini = require('./find-gemini'),

    Tests = require('./tests-model'),
    Index = require('./common/tests-index'),
    EventSource = require('./event-source'),
    reporter = require('./reporter');

function removeTreeIfExists(path) {
    return fs.exists(path)
        .then(function(exists) {
            if (exists) {
                return fs.removeTree(path);
            }
        });
}

function App(options) {
    this.diffDir = temp.path('gemini-gui-diff');
    this.currentDir = temp.path('gemini-gui-curr');
    this._eventSource = new EventSource();

    var Gemini = findGemini();
    this._gemini = new Gemini(options.configFile, {cli: true, env: true});

    var browserIds = this._gemini.browserIds;
    this.referenceDirs = _.zipObject(
        browserIds,
        browserIds.map(function(browserId) {
            return this._gemini.config.forBrowser(browserId).screenshotsDir;
        }.bind(this))
    );
}

App.prototype.addClient = function(connection) {
    this._eventSource.addConnection(connection);
};

App.prototype.sendClientEvent = function(event, data) {
    this._eventSource.emit(event, data);
};

App.prototype.readTests = function(paths, grep) {
    var _this = this;
    if (!this._tests) {
        return this._gemini.readTests(paths, grep)
            .then(function(suiteCollection) {
                _this._tests = new Tests(_this, suiteCollection);
                return _this._tests.data;
            });
    }
    return q.resolve(this._tests.data);
};

App.prototype.run = function(paths, grep) {
    var _this = this;
    this._failedTests = new Index();
    return this._recreateTmpDirs()
        .then(function() {
            return _this._gemini.test(paths, {
                reporters: [reporter(_this), 'flat'],
                tempDir: _this.currentDir,
                grep: grep
            });
        });
};

App.prototype._recreateTmpDirs = function() {
    var _this = this;
    return q.all([removeTreeIfExists(this.currentDir), removeTreeIfExists(this.diffDir)])
        .then(function() {
            return q.all([
                fs.makeDirectory(_this.currentDir),
                fs.makeDirectory(_this.diffDir)
            ]);
        });
};

App.prototype.buildDiff = function(failureReport) {
    var _this = this;
    return this._buildDiffFile(failureReport)
        .then(function(diffPath) {
            return _this.diffPathToURL(diffPath);
        });
};

App.prototype._buildDiffFile = function(failureReport) {
    var diffPath = temp.path({dir: this.diffDir, suffix: '.png'});
    return failureReport.saveDiffTo(diffPath).thenResolve(diffPath);
};

App.prototype.addFailedTest = function(test) {
    this._failedTests.add(test);
};

App.prototype.updateReferenceImage = function(testData) {
    var _this = this,
        test = this._failedTests.find(testData);
    if (!test) {
        return q.reject(new Error('No such test failed'));
    }
    return fs.copy(test.currentPath, test.referencePath)
        .then(function() {
            console.log('Reference image %s has been updated.', test.referencePath);
            // add query string timestamp to avoid caching on client
            return _this.refPathToURL(test.referencePath, test.browserId) + '?t=' + new Date().valueOf();
        });
};

App.prototype.getScreenshotPath = function(suite, stateName, browserId) {
    return this._gemini.getScreenshotPath(suite, stateName, browserId);
};

App.prototype.getBrowserCapabilites = function(browserId) {
    return this._gemini.getBrowserCapabilites(browserId);
};

App.prototype.refPathToURL = function(fullPath, browserId) {
    return this._pathToURL(this.referenceDirs[browserId], fullPath, App.refPrefix + '/' + browserId);
};

App.prototype.currentPathToURL = function(fullPath) {
    return this._pathToURL(this.currentDir, fullPath, App.currentPrefix);
};

App.prototype.diffPathToURL = function(fullPath) {
    return this._pathToURL(this.diffDir, fullPath, App.diffPrefix);
};

App.prototype._pathToURL = function(rootDir, fullPath, prefix) {
    var relPath = path.relative(rootDir, fullPath);
    return prefix + '/' + encodeURI(relPath);
};

App.refPrefix = '/ref';
App.currentPrefix = '/curr';
App.diffPrefix = '/diff';

module.exports = App;
<|endoftext|>


<commit_before>const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const expressSession = require('express-session');
const cookieSession = require('cookie-session');
const morgan = require('morgan');
const favicon = require('serve-favicon');

var nodemailer  = require('nodemailer'),
    express     = require('express'),
    flatten     = require('flatten.js').flatten,
    path        = require('path'),
    app         = express(),
    hbs         = require('./hbs'),
    options     = require('./config'),
    openshift   = require('./addons/openshift')(options), // injects options from openshift env vars
    store       = require('./store')(options.store),
    undefsafe   = require('undefsafe'),
    models      = require('./models').createModels(store),
    routes      = require('./routes'),
    handlers    = require('./handlers'),
    middleware  = require('./middleware'),
    helpers     = require('./helpers'),
    metrics     = require('./metrics'),
    github      = require('./github')(options), // if used, contains github.id
    _           = require('lodash'),
    crypto      = require('crypto'),
    filteredCookieSession = require('./blacklist-cookie'),
    sessionVersion = require('./session-version'),
    stripeRoutes  = require('./stripe')(options),
    flattened;

function generateSessionSecret() {
  console.log('Warning: Generating a session key - please see http://jsbin.com/help/session-secret');
  return crypto.createHash('md5').update(Math.random() + '').digest('hex');
}

/**
 * JS Bin configuration
 */

app.store  = store;
app.sessionVersion = sessionVersion;
// Create model singletons
// models.createModels(store);

app.mailer = (function (mail) {
  var mailTransport = null,
      method   = mail && mail.adapter,
      settings = mail && mail[method];

  if (method && options) {
    mailTransport = nodemailer.createTransport(method, settings);
  }

  return new handlers.MailHandler(mailTransport, app.render.bind(app));
})(options.mail);

app.PRODUCTION  = 'production';
app.DEVELOPMENT = 'development';

// Set the NODE_ENV variable as this is used by Express, we want the
// environment to take precedence but allow it to be set using the config file
// too.
if (process.env.NODE_ENV) {
  options.env = process.env.NODE_ENV;
}
process.env.NODE_ENV = options.env;

// Need to set the node environment to run in the same timezone as the database
// This will ideally be UTC in both cases but if not this can be set either
// using the TZ environment variable or the "timezone" option.
// This is because the mysql library coerces MySQL dates (without timezones)
// into JavaScript date objects.
if (!process.env.TZ && options.timezone) {
  process.env.TZ = options.timezone;
}

// Sort out the port.
(function () {
  var port = process.env.PORT;

  // if we're running from the bin/jsbin file, then we modify
  // both the listen port AND the options.url
  if (process.env.JSBIN_PORT) {
    if (options.url.host.indexOf(':') === -1) {
      options.url.host += ':' + process.env.JSBIN_PORT;
    } else {
      options.url.host = options.url.host.replace(/\:\d+$/, function () {
        return ':' + process.env.JSBIN_PORT;
      });
    }
  }

  if (!port) {
    options.url.host.replace(/\:(\d+)$/, function (m, p) {
      if (p.length) {
        port = p;
      }
    });
  }

  if (!port) {
    port = 80;
  }

  // NOTE: this is *only* used for running the server.listen()
  // it's not used in the urls to access assets, etc.
  options.port = port;
})();

// Strip trailing slash from the prefix
options.url.prefix = options.url.prefix.replace(/\/$/, '');

// Apply the keys from the config file. All nested properties are
// space delimited to match the express style.
//
// For example, app.set('url prefix'); //=> '/'
flattened = flatten(options, ' ');

Object.getOwnPropertyNames(flattened).forEach(function (key) {
  app.set(key, flattened[key]);
});

// in live, we run behind a proxy - so this will give us our IPs again:
// http://expressjs.com/guide.html#proxies
if (process.env.NODE_ENV === app.PRODUCTION || process.env.JSBIN_PROXY) {
  app.set('trust proxy', true);
}

app.disable('x-powered-by');

app.set('root', path.resolve(path.join(__dirname, '..')));

// ensure jsbin is running from the expected root
process.chdir(app.get('root'));

app.set('version', require('../package').version);

app.set('url prefix', options.url.prefix);
app.set('url ssl', options.url.ssl);
app.set('url full', 'http://' + app.get('url host') + app.get('url prefix'));
app.set('basepath', app.get('url prefix'));
app.set('is_production', process.env.NODE_ENV === app.PRODUCTION);

if (options.url.static) {
  app.set('static url', 'http://' + app.get('url static'));
} else {
  app.set('static url', app.get('url full'));
}

if (options.url.runner) {
  // strip trailing slash, just in case
  options.url.runner = options.url.runner.replace(/\/$/, '');
  app.set('url runner', options.url.runner);
} else {
  app.set('url runner', app.get('url full'));
}

app.set('views', 'views');
app.set('view engine', 'html');

app.engine('html', hbs.express3({
  extname: '.html',
  partialsDir: [__dirname + '/../views/partials'],
}));

// app.engine('txt', hbs.express3({
//   extname: '.txt',
//   partialsDir: [
//     path.resolve(__dirname + '/../views/partials')
//   ]
// }));

// Define some global template variables.
var helpers = helpers.createHelpers(app);
app.locals.version = app.get('version');
app.locals.client = app.get('client');
Object.keys(helpers).forEach(function (key) {
  app.locals[key] = helpers[key];
});

// set these as global template variables since they keep getting missed
app.locals.is_production = app.get('is_production');
app.locals.cachebust = app.locals.cacheBust = app.get('is_production') ? '?' + app.get('version') : '';

if (!app.get('session secret')) {
  app.set('session secret', generateSessionSecret());
}

app.connect = function (callback) {
  app.emit('before:connect', app);

  // Register all the middleware.
  var mount = app.set('url prefix') || '/',
      logger;

  logger = process.env.JSBIN_LOGGER || app.get('server logger') || 'tiny';

  if (logger !== 'none') {
    app.use(morgan(logger));
  }

  // favicon
  app.use(favicon(path.join(__dirname, '../', 'public','images','favicon.png')));

  app.use(function (req, res, next) {
    // used for timings
    req.start = Date.now();
    next();
  });
  app.emit('before:middleware');

  app.use(middleware.flash());

  // this middleware says:
  // a) if we have a static url in our config, i.e. static.jsbin.com
  // b) only serve static assets IF AND ONLY IF, the full url is requested,
  //    i.e. static.jsbin.com/css/style.css
  // c) otherwise, skip the static handler, and go straight to routes.js
  //
  // If the request hits routes.js and doesn't match a defined route (or doesn't
  // look like a bin url) then it gets 404'd - i.e. you can't request /css/style.css
  // because it'll return a 404.
  app.use(mount, (function () {
    var staticRouter = express.static(path.join(app.get('root'), 'public'),
      {maxAge: (options['max-age'] || 86400) * 1000});

    return function (req, res, next) {
      // construct the full request url
      var url = req.protocol + '://' + req.get('host') + req.url;

      // *if* the config has a static url path (i.e. different from jsbin.com),
      // i.e. static.jsbin.com
      if (options.url.static) {
        // and the url requested is NOT static, then continue with node
        if (url.indexOf(options.url.static) === -1) {
          return next();
        }
      }
      // otherwise serve it as a static asset...
      return staticRouter(req, res, next);
    };
  })());

  app.use(middleware.limitContentLength({limit: app.get('max-request-size')}));

  app.emit('before:cookies', { app: app });

  app.use(cookieParser(app.get('session secret')));
  app.use(cookieSession({
    keys: ['jsbin'],
    cookie: {
      secure: false, // allows cookie to be exchanged across http + https on purpose
      maxAge: 365 * 24 * 60 * 60 * 1000,
      // the domain must contain a dot and should not have a port
      domain: app.get('url host').indexOf('.') === -1 ? undefined : '.' + app.get('url host').replace(/:\d+$/, '')
    }
  }));

  app.emit('before:session', {app: app});

  // memcached sessions
  if (options.session.memcached && options.session.memcached.connection) {
    require('./addons/memcached')(app, options.session.memcached.connection);
  }

  if (options.url.host !== 'jsbin.com') {
    require('./ping');
  }

  // this needs to be last, because it hooks an onHeaders, which
  // "fires in reverse order" so it needs to go first.
  app.use(filteredCookieSession(['user.settings', 'passport', 'user.email', 'user.github_id', 'user.github_token', 'user.dropbox_token', 'user.dropbox_id', 'user.flagged', 'user.bincount', 'user.api_key', 'user.key', 'user.id', 'user.embed']));

  app.emit('after:session', {app: app});

  app.use(function (req, res, next) {
    // Deletes cookies using the old domain (without the leading '.')
    // and we know this because they don't have a version in their cookie.
    if (undefsafe(req, 'session.version') === undefined) {
      var oldSess = _.extend({ version: app.get('version') }, req.session);
      res.clearCookie('jsbin');
      req.session = oldSess;
    }

    next();
  });

  // Passport
  if (options.github && options.github.id) {
    github.initialize(app);
  }
  if (options.dropbox && options.dropbox.id) {
    require('./dropbox')(options).initialize();
  }

  app.use(bodyParser.urlencoded({ extended: true }));
  app.use(bodyParser.json());

  app.use(middleware.csrf({ ignore: ['/', /^\/api\//, /^\/hooks\//, /^\/subscribe\//] }));
  app.use(middleware.api({ app: app }));
  app.use(middleware.subdomain(app));
  app.use(middleware.noslashes());
  app.use(middleware.protectServiceWorker({ allowed: ['/sw.js', '/sw-runner.js' ]}));
  app.use(middleware.ajax());
  // app.use(middleware.cors());
  app.use(middleware.jsonp());
  app.use(middleware.protocolCheck);
  app.use(middleware.decompressBody);
  app.use(middleware.notices());
  app.use(middleware.nofollow({
    allowed: ['/']
  }));

  app.emit('before:router', app);
  app.use(mount, routes(app));

  // All routes must be registered after middleware
  // Register stripe webhooks
  if (options.payment && options.payment.stripe && options.store.adapter === 'mysql') {
    stripeRoutes(app);
  }
  // Register all routes

  app.emit('after', app);

  store.connect(function (err) {
    if (err) {
      metrics.increment('error.store.connect');
      throw err;
    }

    var port = app.set('port');
    app.listen(port, options.host);	// options.host can be empty, meaning "bind to all network interfaces"

    if (typeof callback === 'function') {
      callback();
    }
    app.emit('connected');

    process.stdout.write('JS Bin v' + app.get('version') + ' is up and running on port ' + app.get('port') + '. Now point your browser at ' + app.get('url full') + '\n');
  });
};

// Export the application to allow it to be included.
module.exports = { app: app };

// Run a local development server if this file is called directly.
if (require.main === module) {
  app.connect();
}
<commit_msg>chore: revert old cookie tweak<commit_after>const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const expressSession = require('express-session');
const cookieSession = require('cookie-session');
const morgan = require('morgan');
const favicon = require('serve-favicon');

var nodemailer  = require('nodemailer'),
    express     = require('express'),
    flatten     = require('flatten.js').flatten,
    path        = require('path'),
    app         = express(),
    hbs         = require('./hbs'),
    options     = require('./config'),
    openshift   = require('./addons/openshift')(options), // injects options from openshift env vars
    store       = require('./store')(options.store),
    undefsafe   = require('undefsafe'),
    models      = require('./models').createModels(store),
    routes      = require('./routes'),
    handlers    = require('./handlers'),
    middleware  = require('./middleware'),
    helpers     = require('./helpers'),
    metrics     = require('./metrics'),
    github      = require('./github')(options), // if used, contains github.id
    _           = require('lodash'),
    crypto      = require('crypto'),
    filteredCookieSession = require('./blacklist-cookie'),
    sessionVersion = require('./session-version'),
    stripeRoutes  = require('./stripe')(options),
    flattened;

function generateSessionSecret() {
  console.log('Warning: Generating a session key - please see http://jsbin.com/help/session-secret');
  return crypto.createHash('md5').update(Math.random() + '').digest('hex');
}

/**
 * JS Bin configuration
 */

app.store  = store;
app.sessionVersion = sessionVersion;
// Create model singletons
// models.createModels(store);

app.mailer = (function (mail) {
  var mailTransport = null,
      method   = mail && mail.adapter,
      settings = mail && mail[method];

  if (method && options) {
    mailTransport = nodemailer.createTransport(method, settings);
  }

  return new handlers.MailHandler(mailTransport, app.render.bind(app));
})(options.mail);

app.PRODUCTION  = 'production';
app.DEVELOPMENT = 'development';

// Set the NODE_ENV variable as this is used by Express, we want the
// environment to take precedence but allow it to be set using the config file
// too.
if (process.env.NODE_ENV) {
  options.env = process.env.NODE_ENV;
}
process.env.NODE_ENV = options.env;

// Need to set the node environment to run in the same timezone as the database
// This will ideally be UTC in both cases but if not this can be set either
// using the TZ environment variable or the "timezone" option.
// This is because the mysql library coerces MySQL dates (without timezones)
// into JavaScript date objects.
if (!process.env.TZ && options.timezone) {
  process.env.TZ = options.timezone;
}

// Sort out the port.
(function () {
  var port = process.env.PORT;

  // if we're running from the bin/jsbin file, then we modify
  // both the listen port AND the options.url
  if (process.env.JSBIN_PORT) {
    if (options.url.host.indexOf(':') === -1) {
      options.url.host += ':' + process.env.JSBIN_PORT;
    } else {
      options.url.host = options.url.host.replace(/\:\d+$/, function () {
        return ':' + process.env.JSBIN_PORT;
      });
    }
  }

  if (!port) {
    options.url.host.replace(/\:(\d+)$/, function (m, p) {
      if (p.length) {
        port = p;
      }
    });
  }

  if (!port) {
    port = 80;
  }

  // NOTE: this is *only* used for running the server.listen()
  // it's not used in the urls to access assets, etc.
  options.port = port;
})();

// Strip trailing slash from the prefix
options.url.prefix = options.url.prefix.replace(/\/$/, '');

// Apply the keys from the config file. All nested properties are
// space delimited to match the express style.
//
// For example, app.set('url prefix'); //=> '/'
flattened = flatten(options, ' ');

Object.getOwnPropertyNames(flattened).forEach(function (key) {
  app.set(key, flattened[key]);
});

// in live, we run behind a proxy - so this will give us our IPs again:
// http://expressjs.com/guide.html#proxies
if (process.env.NODE_ENV === app.PRODUCTION || process.env.JSBIN_PROXY) {
  app.set('trust proxy', true);
}

app.disable('x-powered-by');

app.set('root', path.resolve(path.join(__dirname, '..')));

// ensure jsbin is running from the expected root
process.chdir(app.get('root'));

app.set('version', require('../package').version);

app.set('url prefix', options.url.prefix);
app.set('url ssl', options.url.ssl);
app.set('url full', 'http://' + app.get('url host') + app.get('url prefix'));
app.set('basepath', app.get('url prefix'));
app.set('is_production', process.env.NODE_ENV === app.PRODUCTION);

if (options.url.static) {
  app.set('static url', 'http://' + app.get('url static'));
} else {
  app.set('static url', app.get('url full'));
}

if (options.url.runner) {
  // strip trailing slash, just in case
  options.url.runner = options.url.runner.replace(/\/$/, '');
  app.set('url runner', options.url.runner);
} else {
  app.set('url runner', app.get('url full'));
}

app.set('views', 'views');
app.set('view engine', 'html');

app.engine('html', hbs.express3({
  extname: '.html',
  partialsDir: [__dirname + '/../views/partials'],
}));

// app.engine('txt', hbs.express3({
//   extname: '.txt',
//   partialsDir: [
//     path.resolve(__dirname + '/../views/partials')
//   ]
// }));

// Define some global template variables.
var helpers = helpers.createHelpers(app);
app.locals.version = app.get('version');
app.locals.client = app.get('client');
Object.keys(helpers).forEach(function (key) {
  app.locals[key] = helpers[key];
});

// set these as global template variables since they keep getting missed
app.locals.is_production = app.get('is_production');
app.locals.cachebust = app.locals.cacheBust = app.get('is_production') ? '?' + app.get('version') : '';

if (!app.get('session secret')) {
  app.set('session secret', generateSessionSecret());
}

app.connect = function (callback) {
  app.emit('before:connect', app);

  // Register all the middleware.
  var mount = app.set('url prefix') || '/',
      logger;

  logger = process.env.JSBIN_LOGGER || app.get('server logger') || 'tiny';

  if (logger !== 'none') {
    app.use(morgan(logger));
  }

  // favicon
  app.use(favicon(path.join(__dirname, '../', 'public','images','favicon.png')));

  app.use(function (req, res, next) {
    // used for timings
    req.start = Date.now();
    next();
  });
  app.emit('before:middleware');

  app.use(middleware.flash());

  // this middleware says:
  // a) if we have a static url in our config, i.e. static.jsbin.com
  // b) only serve static assets IF AND ONLY IF, the full url is requested,
  //    i.e. static.jsbin.com/css/style.css
  // c) otherwise, skip the static handler, and go straight to routes.js
  //
  // If the request hits routes.js and doesn't match a defined route (or doesn't
  // look like a bin url) then it gets 404'd - i.e. you can't request /css/style.css
  // because it'll return a 404.
  app.use(mount, (function () {
    var staticRouter = express.static(path.join(app.get('root'), 'public'),
      {maxAge: (options['max-age'] || 86400) * 1000});

    return function (req, res, next) {
      // construct the full request url
      var url = req.protocol + '://' + req.get('host') + req.url;

      // *if* the config has a static url path (i.e. different from jsbin.com),
      // i.e. static.jsbin.com
      if (options.url.static) {
        // and the url requested is NOT static, then continue with node
        if (url.indexOf(options.url.static) === -1) {
          return next();
        }
      }
      // otherwise serve it as a static asset...
      return staticRouter(req, res, next);
    };
  })());

  app.use(middleware.limitContentLength({limit: app.get('max-request-size')}));

  app.emit('before:cookies', { app: app });

  app.use(cookieParser(app.get('session secret')));
  app.use(cookieSession({
    keys: ['jsbin'],
    cookie: {
      maxAge: 365 * 24 * 60 * 60 * 1000,
      // the domain must contain a dot and should not have a port
      domain: app.get('url host').indexOf('.') === -1 ? undefined : '.' + app.get('url host').replace(/:\d+$/, '')
    }
  }));

  app.emit('before:session', {app: app});

  // memcached sessions
  if (options.session.memcached && options.session.memcached.connection) {
    require('./addons/memcached')(app, options.session.memcached.connection);
  }

  if (options.url.host !== 'jsbin.com') {
    require('./ping');
  }

  // this needs to be last, because it hooks an onHeaders, which
  // "fires in reverse order" so it needs to go first.
  app.use(filteredCookieSession(['user.settings', 'passport', 'user.email', 'user.github_id', 'user.github_token', 'user.dropbox_token', 'user.dropbox_id', 'user.flagged', 'user.bincount', 'user.api_key', 'user.key', 'user.id', 'user.embed']));

  app.emit('after:session', {app: app});

  app.use(function (req, res, next) {
    // Deletes cookies using the old domain (without the leading '.')
    // and we know this because they don't have a version in their cookie.
    if (undefsafe(req, 'session.version') === undefined) {
      var oldSess = _.extend({ version: app.get('version') }, req.session);
      res.clearCookie('jsbin');
      req.session = oldSess;
    }

    next();
  });

  // Passport
  if (options.github && options.github.id) {
    github.initialize(app);
  }
  if (options.dropbox && options.dropbox.id) {
    require('./dropbox')(options).initialize();
  }

  app.use(bodyParser.urlencoded({ extended: true }));
  app.use(bodyParser.json());

  app.use(middleware.csrf({ ignore: ['/', /^\/api\//, /^\/hooks\//, /^\/subscribe\//] }));
  app.use(middleware.api({ app: app }));
  app.use(middleware.subdomain(app));
  app.use(middleware.noslashes());
  app.use(middleware.protectServiceWorker({ allowed: ['/sw.js', '/sw-runner.js' ]}));
  app.use(middleware.ajax());
  // app.use(middleware.cors());
  app.use(middleware.jsonp());
  app.use(middleware.protocolCheck);
  app.use(middleware.decompressBody);
  app.use(middleware.notices());
  app.use(middleware.nofollow({
    allowed: ['/']
  }));

  app.emit('before:router', app);
  app.use(mount, routes(app));

  // All routes must be registered after middleware
  // Register stripe webhooks
  if (options.payment && options.payment.stripe && options.store.adapter === 'mysql') {
    stripeRoutes(app);
  }
  // Register all routes

  app.emit('after', app);

  store.connect(function (err) {
    if (err) {
      metrics.increment('error.store.connect');
      throw err;
    }

    var port = app.set('port');
    app.listen(port, options.host);	// options.host can be empty, meaning "bind to all network interfaces"

    if (typeof callback === 'function') {
      callback();
    }
    app.emit('connected');

    process.stdout.write('JS Bin v' + app.get('version') + ' is up and running on port ' + app.get('port') + '. Now point your browser at ' + app.get('url full') + '\n');
  });
};

// Export the application to allow it to be included.
module.exports = { app: app };

// Run a local development server if this file is called directly.
if (require.main === module) {
  app.connect();
}
<|endoftext|>


<commit_before>var http   = require("http"),
    url    = require("url"),
    wompt  = require("./includes"),
    io     = require("socket.io"),
    logger = wompt.logger,
    mongodb = wompt.db,
    express = require("express");

function App(options){
	this.channels = {};
	this.config = options.config;
	this.pretty_print_config();
	this.clients = new wompt.ClientPool();
	this.express = this.create_express_server();

	this.socket_middleware = [
		express.cookieDecoder()
	];
}

App.prototype = {
	pretty_print_config: function(){
		logger.log("Wompt.com started with config: ");
		logger.dir(this.config);
	},

	create_express_server:function(){
		var config = this.config;
		var exp = express.createServer();
		var me = this;

		exp.configure(function(){
			exp.use(me.statusMiddleware());
			exp.use(express.logger({format: ':method :url :status :response-time' }));
			exp.use(express.cookieDecoder());
			exp.use(express.staticProvider(config.public_dir));
		});

		exp.set('views', wompt.env.root + '/views');
		exp.set('view engine', 'jade');

		exp.get("/chat/:channel", function(req, res, params){
			wompt.Auth.get_or_set_token(req, res);

			res.render('chat', {
				locals:{
					channel: req.params.channel
				}
			});
		});

		exp.get("/", function(req, res, params){
			wompt.Auth.get_or_set_token(req, res);
			res.render('index', {
				locals:{
					url: req.url
				}
			});
		});

		exp.post("/", express.bodyDecoder(), function(req, res, params){
			wompt.Auth.get_or_set_token(req, res);
			res.redirect('/chat/' + req.body.channel);
		});

		exp.get("/users/new", function(req, res, params){
			res.render('users/new');
		});

		exp.post("/users/new", express.bodyDecoder(), function(req, res, params){
			b = req.body;
			var user = new wompt.User({
				name: b.name
			});
			user.save();

			wompt.Auth.start_session(res, user);
			res.redirect('/chat/room1');
		});

		exp.get("/users/sign_in", function(req, res, params){
			res.render('users/sign_in');
		});

		exp.post("/users/sign_in", express.bodyDecoder(), function(req, res, params){
			wompt.Auth.sign_in_user(req.body, {success:function(user){
				wompt.Auth.start_session(res, user);
				res.redirect('/chat/room1');
			}});
		});

		exp.get("/support", function(req, res, params){
			res.render('support');
		});

		exp.get("/terms", function(req, res, params){
			res.render('terms');
		});

		exp.get("/privacy", function(req, res, params){
			res.render('privacy');
		});

		return exp;
	},

	start_server: function(){
		this.express.listen(this.config.port, this.config.host);
		this.start_socket_io();

		logger.log("Starting server on port: " + this.config.port);
	},

	start_socket_io: function(){
		if(this.socket) return;
		var app = this;

		this.socket = io.listen(this.express);
		this.socket.on('connection', function(client){
			logger.log("Connection from: " + client.sessionId);
			app.apply_socket_middleware(client);

			app.clients.add(client);

			client.once('message', function(data){
				logger.log('Handing off client:' + client.sessionId + ' to Channel: ' + data.chan)
				var channel = app.get_channel(data.chan);
				if(channel){
					channel.add_client(client);
				}
			});

			wompt.Auth.authenticate_client(client, {

				authenticated:function(client, user){
					logger.log("Authenticated as user: " + user.name);
					user = user.wrap();
					client.user = user;
					user.clients.add(client);
				},

				anonymous:function(client){
					client.anonymous = true;
					var user = new wompt.User();
					user = user.wrap();
					client.user = user;
					user.clients.add(client);
				}
			})
		});
	},

	apply_socket_middleware: function(client){
		var cont = true,
		    req = client.request,
		    res = client.response;

		for(var i=0, len=this.socket_middleware.length;i<len; i++){
			if(!cont) break;
			this.socket_middleware[i](client.request, client.response, function(){cont = true;});
		}
	},

	get_channel: function(name){
		var channel = this.channels[name];
		if(!channel) channel = this.create_channel(name);
		return channel;
	},

	create_channel: function(name){
		var channel = new wompt.Channel({name: name});
		channel.app = this;
		this.channels[name] = channel;
		return channel;
	},

	statusMiddleware: function(){
		return function(req, res, next){
			if(req.url == '/ok'){
				res.send("OK", 200);
				res.end();
			}else{
				next();
			}
		};
	}
}

module.exports = App;
<commit_msg>Start of standard_page_vars function.<commit_after>var http   = require("http"),
    url    = require("url"),
    wompt  = require("./includes"),
    io     = require("socket.io"),
    logger = wompt.logger,
    mongodb = wompt.db,
    express = require("express");

function App(options){
	this.channels = {};
	this.config = options.config;
	this.pretty_print_config();
	this.clients = new wompt.ClientPool();
	this.express = this.create_express_server();

	this.socket_middleware = [
		express.cookieDecoder()
	];
}

App.prototype = {
	pretty_print_config: function(){
		logger.log("Wompt.com started with config: ");
		logger.dir(this.config);
	},

	create_express_server:function(){
		var config = this.config;
		var exp = express.createServer();
		var me = this;

		exp.configure(function(){
			exp.use(me.statusMiddleware());
			exp.use(express.logger({format: ':method :url :status :response-time' }));
			exp.use(express.cookieDecoder());
			exp.use(express.staticProvider(config.public_dir));
		});

		exp.set('views', wompt.env.root + '/views');
		exp.set('view engine', 'jade');

		exp.get("/chat/:channel", function(req, res, params){
			wompt.Auth.get_or_set_token(req, res);

			res.render('chat', {
				locals:{
					channel: req.params.channel
				}
			});
		});

		exp.get("/", function(req, res, params){
			wompt.Auth.get_or_set_token(req, res);
			var vars = me.standard_page_vars(req);
			res.render('index', {
				locals: vars
			});
		});

		exp.post("/", express.bodyDecoder(), function(req, res, params){
			wompt.Auth.get_or_set_token(req, res);
			res.redirect('/chat/' + req.body.channel);
		});

		exp.get("/users/new", function(req, res, params){
			res.render('users/new');
		});

		exp.post("/users/new", express.bodyDecoder(), function(req, res, params){
			b = req.body;
			var user = new wompt.User({
				name: b.name
			});
			user.save();

			wompt.Auth.start_session(res, user);
			res.redirect('/chat/room1');
		});

		exp.get("/users/sign_in", function(req, res, params){
			res.render('users/sign_in');
		});

		exp.post("/users/sign_in", express.bodyDecoder(), function(req, res, params){
			wompt.Auth.sign_in_user(req.body, {success:function(user){
				wompt.Auth.start_session(res, user);
				res.redirect('/chat/room1');
			}});
		});

		exp.get("/support", function(req, res, params){
			res.render('support');
		});

		exp.get("/terms", function(req, res, params){
			res.render('terms');
		});

		exp.get("/privacy", function(req, res, params){
			res.render('privacy');
		});

		return exp;
	},

	standard_page_vars: function(req){
		var vars = {
			url: req.url,

		};
		return vars;
	},

	start_server: function(){
		this.express.listen(this.config.port, this.config.host);
		this.start_socket_io();

		logger.log("Starting server on port: " + this.config.port);
	},

	start_socket_io: function(){
		if(this.socket) return;
		var app = this;

		this.socket = io.listen(this.express);
		this.socket.on('connection', function(client){
			logger.log("Connection from: " + client.sessionId);
			app.apply_socket_middleware(client);

			app.clients.add(client);

			client.once('message', function(data){
				logger.log('Handing off client:' + client.sessionId + ' to Channel: ' + data.chan)
				var channel = app.get_channel(data.chan);
				if(channel){
					channel.add_client(client);
				}
			});

			wompt.Auth.authenticate_client(client, {

				authenticated:function(client, user){
					logger.log("Authenticated as user: " + user.name);
					user = user.wrap();
					client.user = user;
					user.clients.add(client);
				},

				anonymous:function(client){
					client.anonymous = true;
					var user = new wompt.User();
					user = user.wrap();
					client.user = user;
					user.clients.add(client);
				}
			})
		});
	},

	apply_socket_middleware: function(client){
		var cont = true,
		    req = client.request,
		    res = client.response;

		for(var i=0, len=this.socket_middleware.length;i<len; i++){
			if(!cont) break;
			this.socket_middleware[i](client.request, client.response, function(){cont = true;});
		}
	},

	get_channel: function(name){
		var channel = this.channels[name];
		if(!channel) channel = this.create_channel(name);
		return channel;
	},

	create_channel: function(name){
		var channel = new wompt.Channel({name: name});
		channel.app = this;
		this.channels[name] = channel;
		return channel;
	},

	statusMiddleware: function(){
		return function(req, res, next){
			if(req.url == '/ok'){
				res.send("OK", 200);
				res.end();
			}else{
				next();
			}
		};
	}
}

module.exports = App;
<|endoftext|>


<commit_before>var util = require('util');
var spawn = require('child_process').spawn;

/**
 * Read the MAC address from the ARP table.
 *
 * 3 methods for lin/win/mac  Linux reads /proc/net/arp
 * mac and win read the output of the arp command.
 *
 * all 3 ping the IP first without checking the response to encourage the
 * OS to update the arp table.
 *
 * 26/08/2013 -- Changelog by Leandre Gohy (leandre.gohy@hexeo.be)
 * - FIX : arp command for OSX (-n not -an)
 * - MODIFY : rewrite Linux lookup function to avoid looping over all entries and returned lines (arp -n IPADDRESS)
 * - MODIFY : rewrite OSX lookup function to avoid looping over all returned lines
 * - FIX : OSX formates double zero as a single one (i.e : 0:19:99:50:3a:3 instead of 00:19:99:50:3a:3)
 * - FIX : lookup functions did not returns the function on error causing callback to be called twice
 * - FIX : Windows lookup function returns wrong mac address due to indexOf usage (192.168.1.1 -> 192.168.1.10)
 *
 */
module.exports.getMAC = function(ipaddress, cb) {
	if(process.platform.indexOf('linux') == 0) {
		exports.readMACLinux(ipaddress, cb);
	}
	else if (process.platform.indexOf('win') == 0) {
		exports.readMACWindows(ipaddress, cb);
	}
	else if (process.platform.indexOf('darwin') == 0) {
		exports.readMACMac(ipaddress, cb);
	}
};

/**
 * read from arp -n IPADDRESS
 */
module.exports.readMACLinux = function(ipaddress, cb) {

	// ping the ip address to encourage the kernel to populate the arp tables
	var ping = spawn("ping", [ "-c", "1", ipaddress ]);

	ping.on('close', function (code) {
		// not bothered if ping did not work

		var arp = spawn("arp", [ "-n", ipaddress ]);
		var buffer = '';
		var errstream = '';
		arp.stdout.on('data', function (data) {
			buffer += data;
		});
		arp.stderr.on('data', function (data) {
			errstream += data;
		});

		arp.on('close', function (code) {
			if (code !== 0) {
				console.log("Error running arp " + code + " " + errstream);
				cb(true, code);
				return;
			}

			//Parse this format
			//Lookup succeeded : Address                  HWtype  HWaddress           Flags Mask            Iface
			//					IPADDRESS	              ether   MACADDRESS   C                     IFACE
			//Lookup failed : HOST (IPADDRESS) -- no entry
			//There is minimum two lines when lookup is successful
			var table = buffer.split('\n');
			if (table.length >= 2) {
				var parts = table[1].split(' ').filter(String);
				cb(false, parts[2]);
				return;
			}
			cb(true, "Could not find ip in arp table: " + ipaddress);
		});
	});

};

/**
 * read from arp -a IPADDRESS
 */
module.exports.readMACWindows = function(ipaddress, cb) {

	// ping the ip address to encourage the kernel to populate the arp tables
	var ping = spawn("ping", ["-c", "1", ipaddress ]);

	ping.on('close', function (code) {
		// not bothered if ping did not work

		var arp = spawn("arp", ["-a", ipaddress] );
		var buffer = '';
		var errstream = '';
		var lineIndex;

		arp.stdout.on('data', function (data) {
			buffer += data;
		});
		arp.stderr.on('data', function (data) {
			errstream += data;
		});

		arp.on('close', function (code) {
			if (code !== 0) {
				console.log("Error running arp " + code + " " + errstream);
				cb(true, code);
				return;
			}

			var table = buffer.split('\n\r');
			for (lineIndex = 3; lineIndex < table.length; lineIndex++) {
				//parse this format
				//[blankline]
				//Interface: 192.º68.1.54
				//  Internet Address      Physical Address     Type
				//  192.168.1.1           50-67-f0-8c-7a-3f    dynamic

				var parts = table[lineIndex].split(' ').filter(String);
				if (parts[0] === ipaddress) {
					var mac = parts[1].replace(/-/g, ':');
					cb(false, mac);
					return;
				}
			}
			cb(true, "Count not find ip in arp table: " + ipaddress);
		});
	});

};
/**
 * read from arp -n IPADDRESS
 */
module.exports.readMACMac = function(ipaddress, cb) {

	// ping the ip address to encourage the kernel to populate the arp tables
	var ping = spawn("ping", ["-n", "1", ipaddress ]);

	ping.on('close', function (code) {
		// not bothered if ping did not work

		var arp = spawn("arp", ["-n", ipaddress] );
		var buffer = '';
		var errstream = '';
		arp.stdout.on('data', function (data) {
			buffer += data;
		});
		arp.stderr.on('data', function (data) {
			errstream += data;
		});

		arp.on('close', function (code) {
			//On lookup failed OSX returns code 1
			if (code !== 0) {
				console.log("Error running arp " + code + " " + errstream);
				cb(true, code);
				return;
			}

			//parse this format
			//Lookup succeeded : HOST (IPADDRESS) at MACADDRESS on IFACE ifscope [ethernet]
			//Lookup failed : HOST (IPADDRESS) -- no entry
			var parts = buffer.split(' ').filter(String);
			if (parts[3] !== 'no') {
				var mac = parts[3].replace(/^0:/g, '00:').replace(/:0:/g, ':00:').replace(/:0$/g, ':00');
				cb(false, mac);
				return;
			}

			cb(true, "Count not find ip in arp table: " + ipaddress);
		});
	});

};
<commit_msg>swapped \n\r to \r\n for windows readMAC<commit_after>var util = require('util');
var spawn = require('child_process').spawn;

/**
 * Read the MAC address from the ARP table.
 *
 * 3 methods for lin/win/mac  Linux reads /proc/net/arp
 * mac and win read the output of the arp command.
 *
 * all 3 ping the IP first without checking the response to encourage the
 * OS to update the arp table.
 *
 * 26/08/2013 -- Changelog by Leandre Gohy (leandre.gohy@hexeo.be)
 * - FIX : arp command for OSX (-n not -an)
 * - MODIFY : rewrite Linux lookup function to avoid looping over all entries and returned lines (arp -n IPADDRESS)
 * - MODIFY : rewrite OSX lookup function to avoid looping over all returned lines
 * - FIX : OSX formates double zero as a single one (i.e : 0:19:99:50:3a:3 instead of 00:19:99:50:3a:3)
 * - FIX : lookup functions did not returns the function on error causing callback to be called twice
 * - FIX : Windows lookup function returns wrong mac address due to indexOf usage (192.168.1.1 -> 192.168.1.10)
 *
 */
module.exports.getMAC = function(ipaddress, cb) {
	if(process.platform.indexOf('linux') == 0) {
		exports.readMACLinux(ipaddress, cb);
	}
	else if (process.platform.indexOf('win') == 0) {
		exports.readMACWindows(ipaddress, cb);
	}
	else if (process.platform.indexOf('darwin') == 0) {
		exports.readMACMac(ipaddress, cb);
	}
};

/**
 * read from arp -n IPADDRESS
 */
module.exports.readMACLinux = function(ipaddress, cb) {

	// ping the ip address to encourage the kernel to populate the arp tables
	var ping = spawn("ping", [ "-c", "1", ipaddress ]);

	ping.on('close', function (code) {
		// not bothered if ping did not work

		var arp = spawn("arp", [ "-n", ipaddress ]);
		var buffer = '';
		var errstream = '';
		arp.stdout.on('data', function (data) {
			buffer += data;
		});
		arp.stderr.on('data', function (data) {
			errstream += data;
		});

		arp.on('close', function (code) {
			if (code !== 0) {
				console.log("Error running arp " + code + " " + errstream);
				cb(true, code);
				return;
			}

			//Parse this format
			//Lookup succeeded : Address                  HWtype  HWaddress           Flags Mask            Iface
			//					IPADDRESS	              ether   MACADDRESS   C                     IFACE
			//Lookup failed : HOST (IPADDRESS) -- no entry
			//There is minimum two lines when lookup is successful
			var table = buffer.split('\n');
			if (table.length >= 2) {
				var parts = table[1].split(' ').filter(String);
				cb(false, parts[2]);
				return;
			}
			cb(true, "Could not find ip in arp table: " + ipaddress);
		});
	});

};

/**
 * read from arp -a IPADDRESS
 */
module.exports.readMACWindows = function(ipaddress, cb) {

	// ping the ip address to encourage the kernel to populate the arp tables
	var ping = spawn("ping", ["-c", "1", ipaddress ]);

	ping.on('close', function (code) {
		// not bothered if ping did not work

		var arp = spawn("arp", ["-a", ipaddress] );
		var buffer = '';
		var errstream = '';
		var lineIndex;

		arp.stdout.on('data', function (data) {
			buffer += data;
		});
		arp.stderr.on('data', function (data) {
			errstream += data;
		});

		arp.on('close', function (code) {
			if (code !== 0) {
				console.log("Error running arp " + code + " " + errstream);
				cb(true, code);
				return;
			}

			var table = buffer.split('\r\n');
			for (lineIndex = 3; lineIndex < table.length; lineIndex++) {
				//parse this format
				//[blankline]
				//Interface: 192.º68.1.54
				//  Internet Address      Physical Address     Type
				//  192.168.1.1           50-67-f0-8c-7a-3f    dynamic

				var parts = table[lineIndex].split(' ').filter(String);
				if (parts[0] === ipaddress) {
					var mac = parts[1].replace(/-/g, ':');
					cb(false, mac);
					return;
				}
			}
			cb(true, "Count not find ip in arp table: " + ipaddress);
		});
	});

};
/**
 * read from arp -n IPADDRESS
 */
module.exports.readMACMac = function(ipaddress, cb) {

	// ping the ip address to encourage the kernel to populate the arp tables
	var ping = spawn("ping", ["-n", "1", ipaddress ]);

	ping.on('close', function (code) {
		// not bothered if ping did not work

		var arp = spawn("arp", ["-n", ipaddress] );
		var buffer = '';
		var errstream = '';
		arp.stdout.on('data', function (data) {
			buffer += data;
		});
		arp.stderr.on('data', function (data) {
			errstream += data;
		});

		arp.on('close', function (code) {
			//On lookup failed OSX returns code 1
			if (code !== 0) {
				console.log("Error running arp " + code + " " + errstream);
				cb(true, code);
				return;
			}

			//parse this format
			//Lookup succeeded : HOST (IPADDRESS) at MACADDRESS on IFACE ifscope [ethernet]
			//Lookup failed : HOST (IPADDRESS) -- no entry
			var parts = buffer.split(' ').filter(String);
			if (parts[3] !== 'no') {
				var mac = parts[3].replace(/^0:/g, '00:').replace(/:0:/g, ':00:').replace(/:0$/g, ':00');
				cb(false, mac);
				return;
			}

			cb(true, "Count not find ip in arp table: " + ipaddress);
		});
	});

};
<|endoftext|>


<commit_before>// Copyright (c) 2012-2017, Matt Godbolt
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

var _ = require('underscore-node');
var utils = require('./utils');

function AsmParser(compilerProps) {
    var labelFind = /[.a-zA-Z_][a-zA-Z0-9$_.]*/g;
    var dataDefn = /\.(string|asciz|ascii|[1248]?byte|short|word|long|quad|value|zero)/;
    var fileFind = /^\s*\.file\s+(\d+)\s+"([^"]+)"(\s+"([^"]+)")?.*/;
    var hasOpcodeRe = /^\s*[a-zA-Z]/;
    var definesFunction = /^\s*\.type.*,\s*[@%]function$/;
    var definesGlobal = /^\s*\.globa?l\s*([.a-zA-Z_][a-zA-Z0-9$_.]*)/;
    var labelDef = /^([.a-zA-Z_$][a-zA-Z0-9$_.]*):/;
    const indentedLabelDef = /^\s*([.a-zA-Z_$][a-zA-Z0-9$_.]*):/;
    var assignmentDef = /^\s*([.a-zA-Z_$][a-zA-Z0-9$_.]+)\s*=/;
    var directive = /^\s*\..*$/;
    const startAppBlock = /.*\#APP.*/;
    const endAppBlock = /.*\#NO_APP.*/;
    const startAsmNesting = /.*\# Begin ASM.*/;
    const endAsmNesting = /.*\# End ASM.*/;

    function hasOpcode(line) {
        // Remove any leading label definition...
        var match = line.match(labelDef);
        if (match) {
            line = line.substr(match[0].length);
        }
        // Strip any comments
        line = line.split(/[#;]/, 1)[0];
        // Detect assignment, that's not an opcode...
        if (line.match(assignmentDef)) return false;
        return !!line.match(hasOpcodeRe);
    }

    function filterAsmLine(line, filters) {
        if (!filters.trim) return line;
        var splat = line.split(/\s+/);
        if (splat[0] === "") {
            // An indented line: preserve a two-space indent
            return "  " + splat.slice(1).join(" ");
        } else {
            return splat.join(" ");
        }
    }

    function findUsedLabels(asmLines, filterDirectives) {
        var labelsUsed = {};
        var weakUsages = {};
        var currentLabel = "";
        var inCustomAssembly = 0;

        // Scan through looking for definite label usages (ones used by opcodes),
        // and ones that are weakly used: that is, their use is conditional on another label.
        // For example:
        // .foo: .string "moo"
        // .baz: .quad .foo
        //       mov eax, .baz
        // In this case, the '.baz' is used by an opcode, and so is strongly used.
        // The '.foo' is weakly used by .baz.
        asmLines.forEach(function (line) {
            if (line.match(startAppBlock) || line.match(startAsmNesting)) {
                inCustomAssembly++;
            } else if (line.match(endAppBlock) || line.match(endAsmNesting)) {
                inCustomAssembly--;
            }

            if (inCustomAssembly > 0)
                line = fixLabelIndentation(line);

            var match = line.match(labelDef);
            if (match)
                currentLabel = match[1];

            match = line.match(definesGlobal);
            if (match) {
                labelsUsed[match[1]] = true;
            }
            if (!line || line[0] === '.') return;

            match = line.match(labelFind);
            if (!match) return;

            if (!filterDirectives || hasOpcode(line) || line.match(definesFunction)) {
                // Only count a label as used if it's used by an opcode, or else we're not filtering directives.
                match.forEach(function (label) {
                    labelsUsed[label] = true;
                });
            } else if (currentLabel) {
                // If we have a current label, then any subsequent opcode or data definition's labels are refered to
                // weakly by that label.
                var isDataDefinition = !!line.match(dataDefn);
                var isOpcode = hasOpcode(line);
                if (isDataDefinition || isOpcode) {
                    if (!weakUsages[currentLabel]) weakUsages[currentLabel] = [];
                    match.forEach(function (label) {
                        weakUsages[currentLabel].push(label);
                    });
                }
            }
        });

        // Now follow the chains of used labels, marking any weak references they refer
        // to as also used. We iteratively do this until either no new labels are found,
        // or we hit a limit (only here to prevent a pathological case from hanging).
        function markUsed(label) {
            labelsUsed[label] = true;
        }

        var MaxLabelIterations = 10;
        for (var iter = 0; iter < MaxLabelIterations; ++iter) {
            var toAdd = [];
            _.each(labelsUsed, function (t, label) { // jshint ignore:line
                _.each(weakUsages[label], function (nowused) {
                    if (labelsUsed[nowused]) return;
                    toAdd.push(nowused);
                });
            });
            if (!toAdd) break;
            _.each(toAdd, markUsed);
        }
        return labelsUsed;
    }

    function parseFiles(asmLines) {
        var files = {};
        asmLines.forEach(function (line) {
            var match = line.match(fileFind);
            if (match) {
                var lineNum = parseInt(match[1]);
                if (match[3]) {
                    // Clang-style file directive '.file X "dir" "filename"'
                    files[lineNum] = match[2] + "/" + match[3];
                } else {
                    files[lineNum] = match[2];
                }
            }
        });
        return files;
    }

    function processAsm(asm, filters) {
        if (filters.binary) return processBinaryAsm(asm, filters);

        var result = [];
        var asmLines = utils.splitLines(asm);
        if (filters.preProcessLines !== undefined) {
            asmLines = filters.preProcessLines(asmLines);
        }

        var labelsUsed = findUsedLabels(asmLines, filters.directives);
        var files = parseFiles(asmLines);
        var prevLabel = "";

        var commentOnly = /^\s*(((#|@|\/\/).*)|(\/\*.*\*\/))$/;
        var sourceTag = /^\s*\.loc\s+(\d+)\s+(\d+).*/;
        var sourceStab = /^\s*\.stabn\s+(\d+),0,(\d+),.*/;
        const stdInLooking = /.*<stdin>|^-$|example\.[^/]+$|<source>/;
        var endBlock = /\.(cfi_endproc|data|text|section)/;
        let source = null;

        var inCustomAssembly = 0;
        asmLines.forEach(function (line) {
            let match;
            if (line.trim() === "") {
                result.push({text: "", source: null});
                return;
            }

            if (line.match(startAppBlock) || line.match(startAsmNesting)) {
                inCustomAssembly++;
            } else if (line.match(endAppBlock) || line.match(endAsmNesting)) {
                inCustomAssembly--;
            }

            if (!!(match = line.match(sourceTag))) {
                const file = files[parseInt(match[1])];
                const sourceLine = parseInt(match[2]);
                if (file) {
                    source = {
                        file: !file.match(stdInLooking) ? file : null,
                        line: sourceLine
                    };
                } else {
                    source = null;
                }
            }
            if (!!(match = line.match(sourceStab))) {
                // cf http://www.math.utah.edu/docs/info/stabs_11.html#SEC48
                switch (parseInt(match[1])) {
                    case 68:
                        source = {'file': null, 'line': parseInt(match[2])};
                        break;
                    case 132:
                    case 100:
                        source = null;
                        prevLabel = null;
                        break;
                }
            }
            if (line.match(endBlock)) {
                source = null;
                prevLabel = null;
            }

            if (filters.commentOnly && line.match(commentOnly)) return;

            if (inCustomAssembly > 0)
                line = fixLabelIndentation(line);

            match = line.match(labelDef);

            if (!match) match = line.match(assignmentDef);
            if (match) {
                // It's a label definition.
                if (labelsUsed[match[1]] === undefined) {
                    // It's an unused label.
                    if (filters.labels) return;
                } else {
                    // A used label.
                    prevLabel = match;
                }
            }

            if (!match && filters.directives) {
                // Check for directives only if it wasn't a label; the regexp would
                // otherwise misinterpret labels as directives.
                if (line.match(dataDefn) && prevLabel) {
                    // We're defining data that's being used somewhere.
                } else {
                    if (line.match(directive)) return;
                }
            }

            line = utils.expandTabs(line);
            result.push({text: filterAsmLine(line, filters), source: hasOpcode(line) ? source : null});
        });
        return result;
    }

    function fixLabelIndentation(line) {
        const match = line.match(indentedLabelDef);
        if (match) {
            return line.replace(/^\s+/, "");
        } else {
            return line;
        }
    }

    var binaryHideFuncRe = null;
    var maxAsmLines = 500;

    function isUserFunction(func) {
        if (binaryHideFuncRe === null) return true;

        return !func.match(binaryHideFuncRe);
    }

    function processBinaryAsm(asm, filters) {
        var result = [];
        var asmLines = asm.split("\n");
        var asmOpcodeRe = /^\s*([0-9a-f]+):\s*(([0-9a-f][0-9a-f] ?)+)\s*(.*)/;
        var lineRe = /^(\/[^:]+):([0-9]+).*/;
        var labelRe = /^([0-9a-f]+)\s+<([^>]+)>:$/;
        var destRe = /.*\s([0-9a-f]+)\s+<([^>]+)>$/;
        var source = null;
        var func = null;

        // Handle "error" documents.
        if (asmLines.length === 1 && asmLines[0][0] === '<') {
            return [{text: asmLines[0], source: null}];
        }

        asmLines.forEach(function (line) {
            if (result.length >= maxAsmLines) {
                if (result.length == maxAsmLines) {
                    result.push({text: "[truncated; too many lines]", source: null});
                }
                return;
            }
            var match = line.match(lineRe);
            if (match) {
                source = {'file': null, 'line': parseInt(match[2])};
                return;
            }

            match = line.match(labelRe);
            if (match) {
                func = match[2];
                if (isUserFunction(func)) {
                    result.push({text: func + ":", source: null});
                }
                return;
            }

            if (!func || !isUserFunction(func)) return;

            match = line.match(asmOpcodeRe);
            if (match) {
                var address = parseInt(match[1], 16);
                var opcodes = match[2].split(" ").filter(function (x) {
                    return x;
                });
                var disassembly = " " + filterAsmLine(match[4], filters);
                var links = null;
                var destMatch = line.match(destRe);
                if (destMatch) {
                    links = [{
                        offset: disassembly.indexOf(destMatch[1]),
                        length: destMatch[1].length,
                        to: parseInt(destMatch[1], 16)
                    }];
                }
                result.push({opcodes: opcodes, address: address, text: disassembly, source: source, links: links});
            } else {
                //result.push({text: line, source: null});
            }
        });
        return result;
    }

    if (compilerProps) {
        var binaryHideFuncReValue = compilerProps('binaryHideFuncRe');
        if (binaryHideFuncReValue) binaryHideFuncRe = new RegExp(binaryHideFuncReValue);

        maxAsmLines = compilerProps('maxLinesOfAsm', maxAsmLines);
    }
    this.process = function (asm, filters) {
        return processAsm(asm, filters);
    };
}

module.exports = {
    AsmParser: AsmParser
};
<commit_msg>undo extra lf<commit_after>// Copyright (c) 2012-2017, Matt Godbolt
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

var _ = require('underscore-node');
var utils = require('./utils');

function AsmParser(compilerProps) {
    var labelFind = /[.a-zA-Z_][a-zA-Z0-9$_.]*/g;
    var dataDefn = /\.(string|asciz|ascii|[1248]?byte|short|word|long|quad|value|zero)/;
    var fileFind = /^\s*\.file\s+(\d+)\s+"([^"]+)"(\s+"([^"]+)")?.*/;
    var hasOpcodeRe = /^\s*[a-zA-Z]/;
    var definesFunction = /^\s*\.type.*,\s*[@%]function$/;
    var definesGlobal = /^\s*\.globa?l\s*([.a-zA-Z_][a-zA-Z0-9$_.]*)/;
    var labelDef = /^([.a-zA-Z_$][a-zA-Z0-9$_.]*):/;
    const indentedLabelDef = /^\s*([.a-zA-Z_$][a-zA-Z0-9$_.]*):/;
    var assignmentDef = /^\s*([.a-zA-Z_$][a-zA-Z0-9$_.]+)\s*=/;
    var directive = /^\s*\..*$/;
    const startAppBlock = /.*\#APP.*/;
    const endAppBlock = /.*\#NO_APP.*/;
    const startAsmNesting = /.*\# Begin ASM.*/;
    const endAsmNesting = /.*\# End ASM.*/;

    function hasOpcode(line) {
        // Remove any leading label definition...
        var match = line.match(labelDef);
        if (match) {
            line = line.substr(match[0].length);
        }
        // Strip any comments
        line = line.split(/[#;]/, 1)[0];
        // Detect assignment, that's not an opcode...
        if (line.match(assignmentDef)) return false;
        return !!line.match(hasOpcodeRe);
    }

    function filterAsmLine(line, filters) {
        if (!filters.trim) return line;
        var splat = line.split(/\s+/);
        if (splat[0] === "") {
            // An indented line: preserve a two-space indent
            return "  " + splat.slice(1).join(" ");
        } else {
            return splat.join(" ");
        }
    }

    function findUsedLabels(asmLines, filterDirectives) {
        var labelsUsed = {};
        var weakUsages = {};
        var currentLabel = "";
        var inCustomAssembly = 0;

        // Scan through looking for definite label usages (ones used by opcodes),
        // and ones that are weakly used: that is, their use is conditional on another label.
        // For example:
        // .foo: .string "moo"
        // .baz: .quad .foo
        //       mov eax, .baz
        // In this case, the '.baz' is used by an opcode, and so is strongly used.
        // The '.foo' is weakly used by .baz.
        asmLines.forEach(function (line) {
            if (line.match(startAppBlock) || line.match(startAsmNesting)) {
                inCustomAssembly++;
            } else if (line.match(endAppBlock) || line.match(endAsmNesting)) {
                inCustomAssembly--;
            }

            if (inCustomAssembly > 0)
                line = fixLabelIndentation(line);

            var match = line.match(labelDef);
            if (match)
                currentLabel = match[1];
            match = line.match(definesGlobal);
            if (match) {
                labelsUsed[match[1]] = true;
            }
            if (!line || line[0] === '.') return;

            match = line.match(labelFind);
            if (!match) return;

            if (!filterDirectives || hasOpcode(line) || line.match(definesFunction)) {
                // Only count a label as used if it's used by an opcode, or else we're not filtering directives.
                match.forEach(function (label) {
                    labelsUsed[label] = true;
                });
            } else if (currentLabel) {
                // If we have a current label, then any subsequent opcode or data definition's labels are refered to
                // weakly by that label.
                var isDataDefinition = !!line.match(dataDefn);
                var isOpcode = hasOpcode(line);
                if (isDataDefinition || isOpcode) {
                    if (!weakUsages[currentLabel]) weakUsages[currentLabel] = [];
                    match.forEach(function (label) {
                        weakUsages[currentLabel].push(label);
                    });
                }
            }
        });

        // Now follow the chains of used labels, marking any weak references they refer
        // to as also used. We iteratively do this until either no new labels are found,
        // or we hit a limit (only here to prevent a pathological case from hanging).
        function markUsed(label) {
            labelsUsed[label] = true;
        }

        var MaxLabelIterations = 10;
        for (var iter = 0; iter < MaxLabelIterations; ++iter) {
            var toAdd = [];
            _.each(labelsUsed, function (t, label) { // jshint ignore:line
                _.each(weakUsages[label], function (nowused) {
                    if (labelsUsed[nowused]) return;
                    toAdd.push(nowused);
                });
            });
            if (!toAdd) break;
            _.each(toAdd, markUsed);
        }
        return labelsUsed;
    }

    function parseFiles(asmLines) {
        var files = {};
        asmLines.forEach(function (line) {
            var match = line.match(fileFind);
            if (match) {
                var lineNum = parseInt(match[1]);
                if (match[3]) {
                    // Clang-style file directive '.file X "dir" "filename"'
                    files[lineNum] = match[2] + "/" + match[3];
                } else {
                    files[lineNum] = match[2];
                }
            }
        });
        return files;
    }

    function processAsm(asm, filters) {
        if (filters.binary) return processBinaryAsm(asm, filters);

        var result = [];
        var asmLines = utils.splitLines(asm);
        if (filters.preProcessLines !== undefined) {
            asmLines = filters.preProcessLines(asmLines);
        }

        var labelsUsed = findUsedLabels(asmLines, filters.directives);
        var files = parseFiles(asmLines);
        var prevLabel = "";

        var commentOnly = /^\s*(((#|@|\/\/).*)|(\/\*.*\*\/))$/;
        var sourceTag = /^\s*\.loc\s+(\d+)\s+(\d+).*/;
        var sourceStab = /^\s*\.stabn\s+(\d+),0,(\d+),.*/;
        const stdInLooking = /.*<stdin>|^-$|example\.[^/]+$|<source>/;
        var endBlock = /\.(cfi_endproc|data|text|section)/;
        let source = null;

        var inCustomAssembly = 0;
        asmLines.forEach(function (line) {
            let match;
            if (line.trim() === "") {
                result.push({text: "", source: null});
                return;
            }

            if (line.match(startAppBlock) || line.match(startAsmNesting)) {
                inCustomAssembly++;
            } else if (line.match(endAppBlock) || line.match(endAsmNesting)) {
                inCustomAssembly--;
            }

            if (!!(match = line.match(sourceTag))) {
                const file = files[parseInt(match[1])];
                const sourceLine = parseInt(match[2]);
                if (file) {
                    source = {
                        file: !file.match(stdInLooking) ? file : null,
                        line: sourceLine
                    };
                } else {
                    source = null;
                }
            }
            if (!!(match = line.match(sourceStab))) {
                // cf http://www.math.utah.edu/docs/info/stabs_11.html#SEC48
                switch (parseInt(match[1])) {
                    case 68:
                        source = {'file': null, 'line': parseInt(match[2])};
                        break;
                    case 132:
                    case 100:
                        source = null;
                        prevLabel = null;
                        break;
                }
            }
            if (line.match(endBlock)) {
                source = null;
                prevLabel = null;
            }

            if (filters.commentOnly && line.match(commentOnly)) return;

            if (inCustomAssembly > 0)
                line = fixLabelIndentation(line);

            match = line.match(labelDef);
            if (!match) match = line.match(assignmentDef);
            if (match) {
                // It's a label definition.
                if (labelsUsed[match[1]] === undefined) {
                    // It's an unused label.
                    if (filters.labels) return;
                } else {
                    // A used label.
                    prevLabel = match;
                }
            }

            if (!match && filters.directives) {
                // Check for directives only if it wasn't a label; the regexp would
                // otherwise misinterpret labels as directives.
                if (line.match(dataDefn) && prevLabel) {
                    // We're defining data that's being used somewhere.
                } else {
                    if (line.match(directive)) return;
                }
            }

            line = utils.expandTabs(line);
            result.push({text: filterAsmLine(line, filters), source: hasOpcode(line) ? source : null});
        });
        return result;
    }

    function fixLabelIndentation(line) {
        const match = line.match(indentedLabelDef);
        if (match) {
            return line.replace(/^\s+/, "");
        } else {
            return line;
        }
    }

    var binaryHideFuncRe = null;
    var maxAsmLines = 500;

    function isUserFunction(func) {
        if (binaryHideFuncRe === null) return true;

        return !func.match(binaryHideFuncRe);
    }

    function processBinaryAsm(asm, filters) {
        var result = [];
        var asmLines = asm.split("\n");
        var asmOpcodeRe = /^\s*([0-9a-f]+):\s*(([0-9a-f][0-9a-f] ?)+)\s*(.*)/;
        var lineRe = /^(\/[^:]+):([0-9]+).*/;
        var labelRe = /^([0-9a-f]+)\s+<([^>]+)>:$/;
        var destRe = /.*\s([0-9a-f]+)\s+<([^>]+)>$/;
        var source = null;
        var func = null;

        // Handle "error" documents.
        if (asmLines.length === 1 && asmLines[0][0] === '<') {
            return [{text: asmLines[0], source: null}];
        }

        asmLines.forEach(function (line) {
            if (result.length >= maxAsmLines) {
                if (result.length == maxAsmLines) {
                    result.push({text: "[truncated; too many lines]", source: null});
                }
                return;
            }
            var match = line.match(lineRe);
            if (match) {
                source = {'file': null, 'line': parseInt(match[2])};
                return;
            }

            match = line.match(labelRe);
            if (match) {
                func = match[2];
                if (isUserFunction(func)) {
                    result.push({text: func + ":", source: null});
                }
                return;
            }

            if (!func || !isUserFunction(func)) return;

            match = line.match(asmOpcodeRe);
            if (match) {
                var address = parseInt(match[1], 16);
                var opcodes = match[2].split(" ").filter(function (x) {
                    return x;
                });
                var disassembly = " " + filterAsmLine(match[4], filters);
                var links = null;
                var destMatch = line.match(destRe);
                if (destMatch) {
                    links = [{
                        offset: disassembly.indexOf(destMatch[1]),
                        length: destMatch[1].length,
                        to: parseInt(destMatch[1], 16)
                    }];
                }
                result.push({opcodes: opcodes, address: address, text: disassembly, source: source, links: links});
            } else {
                //result.push({text: line, source: null});
            }
        });
        return result;
    }

    if (compilerProps) {
        var binaryHideFuncReValue = compilerProps('binaryHideFuncRe');
        if (binaryHideFuncReValue) binaryHideFuncRe = new RegExp(binaryHideFuncReValue);

        maxAsmLines = compilerProps('maxLinesOfAsm', maxAsmLines);
    }
    this.process = function (asm, filters) {
        return processAsm(asm, filters);
    };
}

module.exports = {
    AsmParser: AsmParser
};
<|endoftext|>