<commit_before>/*jshint esversion: 6 */
/* globals Buffer*/
var Block = module.exports;

const Fs = require("fs");
const Fse = require("fs-extra");
const Path = require("path");
const Nacl = require("tweetnacl/nacl-fast");
const nThen = require("nthen");

const Util = require("../common-util");

/*
    We assume that the server is secured against MitM attacks
    via HTTPS, and that malicious actors do not have code execution
    capabilities. If they do, we have much more serious problems.

    The capability to replay a block write or remove results in either
    a denial of service for the user whose block was removed, or in the
    case of a write, a rollback to an earlier password.

    Since block modification is destructive, this can result in loss
    of access to the user's drive.

    So long as the detached signature is never observed by a malicious
    party, and the server discards it after proof of knowledge, replays
    are not possible. However, this precludes verification of the signature
    at a later time.

    Despite this, an integrity check is still possible by the original
    author of the block, since we assume that the block will have been
    encrypted with xsalsa20-poly1305 which is authenticated.
*/
var validateLoginBlock = function (Env, publicKey, signature, block, cb) { // FIXME BLOCKS
    // convert the public key to a Uint8Array and validate it
    if (typeof(publicKey) !== 'string') { return void cb('E_INVALID_KEY'); }

    var u8_public_key;
    try {
        u8_public_key = Nacl.util.decodeBase64(publicKey);
    } catch (e) {
        return void cb('E_INVALID_KEY');
    }

    var u8_signature;
    try {
        u8_signature = Nacl.util.decodeBase64(signature);
    } catch (e) {
        Env.Log.error('INVALID_BLOCK_SIGNATURE', e);
        return void cb('E_INVALID_SIGNATURE');
    }

    // convert the block to a Uint8Array
    var u8_block;
    try {
        u8_block = Nacl.util.decodeBase64(block);
    } catch (e) {
        return void cb('E_INVALID_BLOCK');
    }

    // take its hash
    var hash = Nacl.hash(u8_block);

    // validate the signature against the hash of the content
    var verified = Nacl.sign.detached.verify(hash, u8_signature, u8_public_key);

    // existing authentication ensures that users cannot replay old blocks

    // call back with (err) if unsuccessful
    if (!verified) { return void cb("E_COULD_NOT_VERIFY"); }

    return void cb(null, u8_block);
};

var createLoginBlockPath = function (Env, publicKey) { // FIXME BLOCKS
    // prepare publicKey to be used as a file name
    var safeKey = Util.escapeKeyCharacters(publicKey);

    // validate safeKey
    if (typeof(safeKey) !== 'string') {
        return;
    }

    // derive the full path
    // /home/cryptpad/cryptpad/block/fg/fg32kefksjdgjkewrjksdfksjdfsdfskdjfsfd
    return Path.join(Env.paths.block, safeKey.slice(0, 2), safeKey);
};

Block.validateAncestorProof = function (Env, proof, _cb) {
    var cb = Util.once(Util.mkAsync(_cb));
/* prove that you own an existing block by signing for its publicKey */
    try {
        var parsed = JSON.parse(proof);
        var pub = parsed[0];
        var u8_pub = Nacl.util.decodeBase64(pub);
        var sig = parsed[1];
        var u8_sig = Nacl.util.decodeBase64(sig);
        var valid = false;
        nThen(function (w) {
            valid = Nacl.sign.detached.verify(u8_pub, u8_sig, u8_pub);
            if (!valid) {
                w.abort();
                return void cb('E_INVALID_ANCESTOR_PROOF');
            }
            // else fall through to next step
        }).nThen(function (w) {
            var path = createLoginBlockPath(Env, pub);
            Fs.access(path, Fs.constants.F_OK, w(function (err) {
                if (!err) { return; }
                w.abort(); // else
                return void cb("E_MISSING_ANCESTOR");
            }));
        }).nThen(function () {
            cb(void 0, pub);
        });
    } catch (err) {
        return void cb(err);
    }
};

Block.writeLoginBlock = function (Env, safeKey, msg, _cb) { // FIXME BLOCKS
    var cb = Util.once(Util.mkAsync(_cb));
    //console.log(msg);
    var publicKey = msg[0];
    var signature = msg[1];
    var block = msg[2];
    var registrationProof = msg[3];
    var previousKey;

    var validatedBlock, parsed, path;
    nThen(function (w) {
        if (Util.escapeKeyCharacters(publicKey) !== safeKey) {
            w.abort();
            return void cb("INCORRECT_KEY");
        }
    }).nThen(function (w) {
        if (!Env.restrictRegistration) { return; }
        if (!registrationProof) {
            // we allow users with existing blocks to create new ones
            // call back with error if registration is restricted and no proof of an existing block was provided
            w.abort();
            Env.Log.info("BLOCK_REJECTED_REGISTRATION", {
                safeKey: safeKey,
                publicKey: publicKey,
            });
            return cb("E_RESTRICTED");
        }
        Env.validateAncestorProof(registrationProof, w(function (err, provenKey) {
            if (err || !provenKey) { // double check that a key was validated
                w.abort();
                Env.Log.warn('BLOCK_REJECTED_INVALID_ANCESTOR', {
                    error: err,
                });
                return void cb("E_RESTRICTED");
            }
            previousKey = provenKey;
        }));
    }).nThen(function (w) {
        validateLoginBlock(Env, publicKey, signature, block, w(function (e, _validatedBlock) {
            if (e) {
                w.abort();
                return void cb(e);
            }
            if (!(_validatedBlock instanceof Uint8Array)) {
                w.abort();
                return void cb('E_INVALID_BLOCK');
            }

            validatedBlock = _validatedBlock;

            // derive the filepath
            path = createLoginBlockPath(Env, publicKey);

            // make sure the path is valid
            if (typeof(path) !== 'string') {
                return void cb('E_INVALID_BLOCK_PATH');
            }

            parsed = Path.parse(path);
            if (!parsed || typeof(parsed.dir) !== 'string') {
                w.abort();
                return void cb("E_INVALID_BLOCK_PATH_2");
            }
        }));
    }).nThen(function (w) {
        // make sure the path to the file exists
        Fse.mkdirp(parsed.dir, w(function (e) {
            if (e) {
                w.abort();
                cb(e);
            }
        }));
    }).nThen(function () {
        // actually write the block
        Fs.writeFile(path, Buffer.from(validatedBlock), { encoding: "binary", }, function (err) {
            if (err) { return void cb(err); }
            Env.Log.info('BLOCK_WRITE_BY_OWNER', {
                safeKey: safeKey,
                blockId: publicKey,
                isChange: Boolean(registrationProof),
                previousKey: previousKey,
                path: path,
            });
            cb();
        });
    });
};

/*
    When users write a block, they upload the block, and provide
    a signature proving that they deserve to be able to write to
    the location determined by the public key.

    When removing a block, there is nothing to upload, but we need
    to sign something. Since the signature is considered sensitive
    information, we can just sign some constant and use that as proof.

*/
Block.removeLoginBlock = function (Env, safeKey, msg, cb) { // FIXME BLOCKS
    var publicKey = msg[0];
    var signature = msg[1];
    var block = Nacl.util.decodeUTF8('DELETE_BLOCK'); // clients and the server will have to agree on this constant

    nThen(function (w) {
        if (Util.escapeKeyCharacters(publicKey) !== safeKey) {
            w.abort();
            return void cb("INCORRECT_KEY");
        }
    }).nThen(function () {
        validateLoginBlock(Env, publicKey, signature, block, function (e /*::, validatedBlock */) {
            if (e) { return void cb(e); }
            // derive the filepath
            var path = createLoginBlockPath(Env, publicKey);

            // make sure the path is valid
            if (typeof(path) !== 'string') {
                return void cb('E_INVALID_BLOCK_PATH');
            }

            // FIXME COLDSTORAGE
            Fs.unlink(path, function (err) {
                Env.Log.info('DELETION_BLOCK_BY_OWNER_RPC', {
                    publicKey: publicKey,
                    path: path,
                    status: err? String(err): 'SUCCESS',
                });

                if (err) { return void cb(err); }
                cb();
            });
        });
    });
};

<commit_msg>implement block archival<commit_after>/*jshint esversion: 6 */
/* globals Buffer*/
var Block = module.exports;

const Fs = require("fs");
const Fse = require("fs-extra");
const Path = require("path");
const Nacl = require("tweetnacl/nacl-fast");
const nThen = require("nthen");

const Util = require("../common-util");

/*
    We assume that the server is secured against MitM attacks
    via HTTPS, and that malicious actors do not have code execution
    capabilities. If they do, we have much more serious problems.

    The capability to replay a block write or remove results in either
    a denial of service for the user whose block was removed, or in the
    case of a write, a rollback to an earlier password.

    Since block modification is destructive, this can result in loss
    of access to the user's drive.

    So long as the detached signature is never observed by a malicious
    party, and the server discards it after proof of knowledge, replays
    are not possible. However, this precludes verification of the signature
    at a later time.

    Despite this, an integrity check is still possible by the original
    author of the block, since we assume that the block will have been
    encrypted with xsalsa20-poly1305 which is authenticated.
*/
var validateLoginBlock = function (Env, publicKey, signature, block, cb) { // FIXME BLOCKS
    // convert the public key to a Uint8Array and validate it
    if (typeof(publicKey) !== 'string') { return void cb('E_INVALID_KEY'); }

    var u8_public_key;
    try {
        u8_public_key = Nacl.util.decodeBase64(publicKey);
    } catch (e) {
        return void cb('E_INVALID_KEY');
    }

    var u8_signature;
    try {
        u8_signature = Nacl.util.decodeBase64(signature);
    } catch (e) {
        Env.Log.error('INVALID_BLOCK_SIGNATURE', e);
        return void cb('E_INVALID_SIGNATURE');
    }

    // convert the block to a Uint8Array
    var u8_block;
    try {
        u8_block = Nacl.util.decodeBase64(block);
    } catch (e) {
        return void cb('E_INVALID_BLOCK');
    }

    // take its hash
    var hash = Nacl.hash(u8_block);

    // validate the signature against the hash of the content
    var verified = Nacl.sign.detached.verify(hash, u8_signature, u8_public_key);

    // existing authentication ensures that users cannot replay old blocks

    // call back with (err) if unsuccessful
    if (!verified) { return void cb("E_COULD_NOT_VERIFY"); }

    return void cb(null, u8_block);
};

var createLoginBlockPath = function (Env, publicKey) { // FIXME BLOCKS
    // prepare publicKey to be used as a file name
    var safeKey = Util.escapeKeyCharacters(publicKey);

    // validate safeKey
    if (typeof(safeKey) !== 'string') {
        return;
    }

    // derive the full path
    // /home/cryptpad/cryptpad/block/fg/fg32kefksjdgjkewrjksdfksjdfsdfskdjfsfd
    return Path.join(Env.paths.block, safeKey.slice(0, 2), safeKey);
};

var createLoginBlockArchivePath = function (Env, publicKey) {
    // prepare publicKey to be used as a file name
    var safeKey = Util.escapeKeyCharacters(publicKey);

    // validate safeKey
    if (typeof(safeKey) !== 'string') {
        return;
    }

    // derive the full path
    // /home/cryptpad/cryptpad/block/fg/fg32kefksjdgjkewrjksdfksjdfsdfskdjfsfd
    return Path.join(Env.paths.archive, 'block', safeKey.slice(0, 2), safeKey);
};

Block.validateAncestorProof = function (Env, proof, _cb) {
    var cb = Util.once(Util.mkAsync(_cb));
/* prove that you own an existing block by signing for its publicKey */
    try {
        var parsed = JSON.parse(proof);
        var pub = parsed[0];
        var u8_pub = Nacl.util.decodeBase64(pub);
        var sig = parsed[1];
        var u8_sig = Nacl.util.decodeBase64(sig);
        var valid = false;
        nThen(function (w) {
            valid = Nacl.sign.detached.verify(u8_pub, u8_sig, u8_pub);
            if (!valid) {
                w.abort();
                return void cb('E_INVALID_ANCESTOR_PROOF');
            }
            // else fall through to next step
        }).nThen(function (w) {
            var path = createLoginBlockPath(Env, pub);
            Fs.access(path, Fs.constants.F_OK, w(function (err) {
                if (!err) { return; }
                w.abort(); // else
                return void cb("E_MISSING_ANCESTOR");
            }));
        }).nThen(function () {
            cb(void 0, pub);
        });
    } catch (err) {
        return void cb(err);
    }
};

Block.writeLoginBlock = function (Env, safeKey, msg, _cb) { // FIXME BLOCKS
    var cb = Util.once(Util.mkAsync(_cb));
    //console.log(msg);
    var publicKey = msg[0];
    var signature = msg[1];
    var block = msg[2];
    var registrationProof = msg[3];
    var previousKey;

    var validatedBlock, parsed, path;
    nThen(function (w) {
        if (Util.escapeKeyCharacters(publicKey) !== safeKey) {
            w.abort();
            return void cb("INCORRECT_KEY");
        }
    }).nThen(function (w) {
        if (!Env.restrictRegistration) { return; }
        if (!registrationProof) {
            // we allow users with existing blocks to create new ones
            // call back with error if registration is restricted and no proof of an existing block was provided
            w.abort();
            Env.Log.info("BLOCK_REJECTED_REGISTRATION", {
                safeKey: safeKey,
                publicKey: publicKey,
            });
            return cb("E_RESTRICTED");
        }
        Env.validateAncestorProof(registrationProof, w(function (err, provenKey) {
            if (err || !provenKey) { // double check that a key was validated
                w.abort();
                Env.Log.warn('BLOCK_REJECTED_INVALID_ANCESTOR', {
                    error: err,
                });
                return void cb("E_RESTRICTED");
            }
            previousKey = provenKey;
        }));
    }).nThen(function (w) {
        validateLoginBlock(Env, publicKey, signature, block, w(function (e, _validatedBlock) {
            if (e) {
                w.abort();
                return void cb(e);
            }
            if (!(_validatedBlock instanceof Uint8Array)) {
                w.abort();
                return void cb('E_INVALID_BLOCK');
            }

            validatedBlock = _validatedBlock;

            // derive the filepath
            path = createLoginBlockPath(Env, publicKey);

            // make sure the path is valid
            if (typeof(path) !== 'string') {
                return void cb('E_INVALID_BLOCK_PATH');
            }

            parsed = Path.parse(path);
            if (!parsed || typeof(parsed.dir) !== 'string') {
                w.abort();
                return void cb("E_INVALID_BLOCK_PATH_2");
            }
        }));
    }).nThen(function (w) {
        // make sure the path to the file exists
        Fse.mkdirp(parsed.dir, w(function (e) {
            if (e) {
                w.abort();
                cb(e);
            }
        }));
    }).nThen(function () {
        // actually write the block
        Fs.writeFile(path, Buffer.from(validatedBlock), { encoding: "binary", }, function (err) {
            if (err) { return void cb(err); }
            Env.Log.info('BLOCK_WRITE_BY_OWNER', {
                safeKey: safeKey,
                blockId: publicKey,
                isChange: Boolean(registrationProof),
                previousKey: previousKey,
                path: path,
            });
            cb();
        });
    });
};

/*
    When users write a block, they upload the block, and provide
    a signature proving that they deserve to be able to write to
    the location determined by the public key.

    When removing a block, there is nothing to upload, but we need
    to sign something. Since the signature is considered sensitive
    information, we can just sign some constant and use that as proof.

*/
Block.removeLoginBlock = function (Env, safeKey, msg, cb) {
    var publicKey = msg[0];
    var signature = msg[1];
    var block = Nacl.util.decodeUTF8('DELETE_BLOCK'); // clients and the server will have to agree on this constant

    nThen(function (w) {
        if (Util.escapeKeyCharacters(publicKey) !== safeKey) {
            w.abort();
            return void cb("INCORRECT_KEY");
        }
    }).nThen(function () {
        validateLoginBlock(Env, publicKey, signature, block, function (e /*::, validatedBlock */) {
            if (e) { return void cb(e); }
            // derive the filepath
            var currentPath = createLoginBlockPath(Env, publicKey);

            // make sure the path is valid
            if (typeof(currentPath) !== 'string') {
                return void cb('E_INVALID_BLOCK_PATH');
            }

            var archivePath = createLoginBlockArchivePath(Env, publicKey);
            // make sure the path is valid
            if (typeof(archivePath) !== 'string') {
                return void cb('E_INVALID_BLOCK_ARCHIVAL_PATH');
            }

            Fse.move(currentPath, archivePath, {
                overwrite: true,
            }, function (err) {
                Env.Log.info('ARCHIVAL_BLOCK_BY_OWNER_RPC', {
                    publicKey: publicKey,
                    currentPath: currentPath,
                    archivePath: archivePath,
                    status: err? String(err): 'SUCCESS',
                });
                if (err) { return void cb(err); }
                cb();
            });
        });
    });
};

<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');
var async = require('async');
var semver = require('semver');

var schemes = (function() {
  var default_scheme;
  var schememap = {};

  return {
    get: function(name) {
      return schememap[name];
    },
    add: function(name, resolver) {
      schememap[name] = resolver;
      return this;
    },
    default: function(name) {
      if( !arguments.length ) return default_scheme;
      default_scheme = name;
      return this;
    }
  };
})();

schemes.add('bower', require('../installer/bower.js'));
schemes.add('gitlab', require('../installer/npm.js'));
schemes.add('github', require('../installer/npm.js'));
schemes.add('gist', require('../installer/npm.js'));
schemes.add('bitbucket', require('../installer/npm.js'));
schemes.add('npm', require('../installer/npm.js'));
schemes.default('npm');

/**
 * dependency indicator spec
 *
 */

function extractSubDependencies(pkg) {
  var webDependencies = pkg && pkg.webDependencies;
  var pkgs = [];
  Object.keys(webDependencies || {}).forEach(function(k) {
    var expression = webDependencies[k];
    var name = k, scheme, pos = expression.indexOf(':');
    if( ~pos ) {
      scheme = expression.substring(0, pos);
      expression = expression.substring(pos + 1);
    }

    scheme = scheme || schemes.default();

    // is version
    if( ~['*', 'latest'].indexOf(expression) ) {
      pkgs.push(scheme + ':' + k);
    } else if( semver.validRange(expression) ) {
      pkgs.push(scheme + ':' + k + '@' + expression);
    } else {
      pkgs.push(scheme + ':' + name + '[' + expression + ']');
    }
  });
  return pkgs;
}

function parse(expression) {
  var alias = (function() {
    var pos = expression.indexOf('[');
    if( ~pos ) return expression.substring(0, pos);
  })();

  if( alias ) {
    expression = (function() {
      expression = expression.substring(expression.indexOf('[') + 1)
      expression = expression.substring(0, expression.lastIndexOf(']'));
      return expression;
    })();
  }

  var scheme = (function() {
    if( alias && ~alias.indexOf(':') ) return alias.substring(0, alias.indexOf(':'));
    if( expression && ~expression.indexOf(':') ) return expression.substring(0, expression.indexOf(':'));
  })();

  if( alias && ~alias.indexOf(':') ) alias = alias.substring(alias.indexOf(':') + 1);
  if( expression && ~expression.indexOf(':') ) expression = expression.substring(expression.indexOf(':') + 1);

  return {
    scheme: scheme || schemes.default(),
    alias: alias,
    expression: expression
  }
}

var commands = module.exports = {
  get repository() {
    return repository;
  },
  install: function(pkgs, options, done) {
    // load .webmodulesrc file
    var cwd = options.cwd || process.cwd();
    var pkgfile = path.join(process.cwd(), 'package.json');
    var target = path.resolve(cwd, options['module-directory'] || 'web_modules');

    if( !fs.existsSync(target) ) fs.mkdirSync(target);

    if( !pkgs || !pkgs.length ) {
      if( !fs.existsSync(pkgfile) ) return done(new Error('package.json file not found'));
      pkgs = extractSubDependencies(require(pkgfile));
      options.save = false;
    }

    if( !pkgs.length ) return done(null, []);

    //console.log('pkgs', pkgs);

    var installed = [];
    async.eachSeries(pkgs, function(pkg, done) {
      pkg = parse(pkg);

      console.log('- Installing %s from %s', pkg.expression, pkg.scheme);

      var resolver = schemes.get(pkg.scheme);
      if( !resolver ) return done(new Error('not found repository scheme:' + pkg.scheme));

      resolver.install(pkg, {
        target: target
      }, function(err, result) {
        if( err ) return done(err);

        pkg.manifest = result.manifest;
        pkg.directory = result.directory;
        pkg.name = result.name;
        pkg.expression = pkg.scheme === 'npm' ? result.expression : (pkg.scheme + ':' + result.expression);
        pkg.version = result.version;

        installed.push(pkg);

        // search sub web modules in current packages
        var pkgs = extractSubDependencies(pkg.manifest);
        if( !pkgs.length ) return done();

        // install sub web modules
        commands.install(pkgs, {
          cwd: pkg.directory
        }, done);
      });
    }, function(err) {
      if( err ) return done(err);

      if( options.save && fs.existsSync(pkgfile) ) {
        var manifest = require(pkgfile);
        var wd = manifest.webDependencies = manifest.webDependencies || {};
        installed.forEach(function(info) {
          wd[info.name] = info.expression;
        });

        fs.writeFileSync(pkgfile, JSON.stringify(manifest, null, '  '), 'utf8');
      }

      done(null, installed);
    });
    return this;
  },
  uninstall: function(pkgs, options, done) {

  },
  init: function(options, done) {
  }
};<commit_msg>Update install directory config via .webmodulesrc<commit_after>var fs = require('fs-extra');
var path = require('path');
var async = require('async');
var semver = require('semver');

var schemes = (function() {
  var default_scheme;
  var schememap = {};

  return {
    get: function(name) {
      return schememap[name];
    },
    add: function(name, resolver) {
      schememap[name] = resolver;
      return this;
    },
    default: function(name) {
      if( !arguments.length ) return default_scheme;
      default_scheme = name;
      return this;
    }
  };
})();

schemes.add('bower', require('../installer/bower.js'));
schemes.add('gitlab', require('../installer/npm.js'));
schemes.add('github', require('../installer/npm.js'));
schemes.add('gist', require('../installer/npm.js'));
schemes.add('bitbucket', require('../installer/npm.js'));
schemes.add('npm', require('../installer/npm.js'));
schemes.default('npm');

/**
 * dependency indicator spec
 *
 */

function extractSubDependencies(pkg) {
  var webDependencies = pkg && pkg.webDependencies;
  var pkgs = [];
  Object.keys(webDependencies || {}).forEach(function(k) {
    var expression = webDependencies[k];
    var name = k, scheme, pos = expression.indexOf(':');
    if( ~pos ) {
      scheme = expression.substring(0, pos);
      expression = expression.substring(pos + 1);
    }

    scheme = scheme || schemes.default();

    // is version
    if( ~['*', 'latest'].indexOf(expression) ) {
      pkgs.push(scheme + ':' + k);
    } else if( semver.validRange(expression) ) {
      pkgs.push(scheme + ':' + k + '@' + expression);
    } else {
      pkgs.push(scheme + ':' + name + '[' + expression + ']');
    }
  });
  return pkgs;
}

function parse(expression) {
  var alias = (function() {
    var pos = expression.indexOf('[');
    if( ~pos ) return expression.substring(0, pos);
  })();

  if( alias ) {
    expression = (function() {
      expression = expression.substring(expression.indexOf('[') + 1)
      expression = expression.substring(0, expression.lastIndexOf(']'));
      return expression;
    })();
  }

  var scheme = (function() {
    if( alias && ~alias.indexOf(':') ) return alias.substring(0, alias.indexOf(':'));
    if( expression && ~expression.indexOf(':') ) return expression.substring(0, expression.indexOf(':'));
  })();

  if( alias && ~alias.indexOf(':') ) alias = alias.substring(alias.indexOf(':') + 1);
  if( expression && ~expression.indexOf(':') ) expression = expression.substring(expression.indexOf(':') + 1);

  return {
    scheme: scheme || schemes.default(),
    alias: alias,
    expression: expression
  }
}

var commands = module.exports = {
  get repository() {
    return repository;
  },
  install: function(pkgs, options, done) {
    // load .webmodulesrc file
    var cwd = options.cwd || process.cwd();
    var pkgfile = path.join(cwd, 'package.json');
    var rcfile = path.join(cwd, '.webmodulesrc');
    var target = path.resolve(cwd, 'web_modules');

    if( cwd === process.cwd() && fs.existsSync(rcfile) ) {
      var rc = JSON.parse(fs.readFileSync(rcfile));
      if( rc.directory ) target = path.resolve(cwd, rc.directory);
    }

    if( options.directory ) target = path.resolve(cwd, options.directory);

    if( !fs.existsSync(target) ) fs.mkdirsSync(target);

    if( !pkgs || !pkgs.length ) {
      if( !fs.existsSync(pkgfile) ) return done(new Error('package.json file not found'));
      pkgs = extractSubDependencies(require(pkgfile));
      options.save = false;
    }

    if( !pkgs.length ) return done(null, []);

    //console.log('pkgs', pkgs);

    var installed = [];
    async.eachSeries(pkgs, function(pkg, done) {
      pkg = parse(pkg);

      console.log('- Installing %s from %s', pkg.expression, pkg.scheme);

      var resolver = schemes.get(pkg.scheme);
      if( !resolver ) return done(new Error('not found repository scheme:' + pkg.scheme));

      resolver.install(pkg, {
        target: target
      }, function(err, result) {
        if( err ) return done(err);

        pkg.manifest = result.manifest;
        pkg.directory = result.directory;
        pkg.name = result.name;
        pkg.expression = pkg.scheme === 'npm' ? result.expression : (pkg.scheme + ':' + result.expression);
        pkg.version = result.version;

        installed.push(pkg);

        // search sub web modules in current packages
        var pkgs = extractSubDependencies(pkg.manifest);
        if( !pkgs.length ) return done();

        // install sub web modules
        commands.install(pkgs, {
          cwd: pkg.directory
        }, done);
      });
    }, function(err) {
      if( err ) return done(err);

      if( options.save && fs.existsSync(pkgfile) ) {
        var manifest = require(pkgfile);
        var wd = manifest.webDependencies = manifest.webDependencies || {};
        installed.forEach(function(info) {
          wd[info.name] = info.expression;
        });

        fs.writeFileSync(pkgfile, JSON.stringify(manifest, null, '  '), 'utf8');
      }

      done(null, installed);
    });
    return this;
  },
  uninstall: function(pkgs, options, done) {

  },
  init: function(options, done) {
  }
};<|endoftext|>


<commit_before>'use strict';

var express = require('express');
var cookieParser = require('cookie-parser');
var session = require('express-session');
var bodyParser = require('body-parser');
var compress = require('compression');
var log4js = require('log4js');
var path = require('path');
var oneMonth = 2592000000;

/**
 * Config expressJs stack
 *
 * @param {!Object} app
 * @param {Array.<string>} routes
 * @param {!Object} baboon
 * @return {Object}
 */
module.exports = function (app, routes, baboon) {

    var loggers = baboon.loggers;
    var config = baboon.config;
    var rootPath = config.path.root;

    app.engine('html', require('ejs').renderFile);
    app.set('view engine', 'html');

    if (app.get('env') === 'development') {

        // Disable caching of scripts for easier testing
        app.use(function noCache(req, res, next) {

            if (req.url.indexOf('/app/') === 0 || req.url.indexOf('/common/') === 0 ||
                req.url.indexOf('/api/') === 0 || req.url.indexOf('/assets/') === 0) {

                res.header('Cache-Control', 'no-cache, no-store, must-revalidate');
                res.header('Pragma', 'no-cache');
                res.header('Expires', 0);
            }
            next();
        });

        // Logger in development mode
        app.use(log4js.connectLogger(loggers.express, {level: 'auto', format: ':status :method :url'}));

        // Static path for files and partial views
        app.use(express.static(path.join(rootPath, '.tmp')));
        app.use(express.static(path.join(rootPath, 'client')));

        // Path for application views
        app.set('views', rootPath + '/.tmp/views');
    }
    else if (app.get('env') === 'production') {

        // Enable compress
        app.use(compress());

        // Disable caching for rest api
        app.use(function noCache(req, res, next) {

            if (req.url.indexOf('/api/') === 0) {
                res.header('Cache-Control', 'no-cache, no-store, must-revalidate');
                res.header('Pragma', 'no-cache');
                res.header('Expires', 0);
            }
            next();
        });

        // Logger in production mode
        app.use(log4js.connectLogger(loggers.express, {level: 'auto'}));

        // Path for favicon with cache
        //app.use(express.favicon(path.join(rootPath, '.dist', 'public', 'favicon.ico'), {maxAge:oneMonth}));

        // Static path for files with cache
        app.use(express.static(path.join(rootPath, '.dist', 'public'), {maxAge: oneMonth}));

        // Static path for partial views without cache
        app.use(express.static(path.join(rootPath, '.dist', 'views', 'partials')));

        // Path for application views
        app.set('views', rootPath + '/.dist/views');
    }

    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded());
    app.use(cookieParser());
    app.use(session({
        store: baboon.session.getSessionStore(),
        key: config.session.key,
        secret: config.session.secret,
        cookie: {expires: false}
    }));

    // Enable livereload
    if (app.get('env') === 'development' && config.livereload) {
        app.use(require('connect-livereload')());
    }

    app.use('/account/activate/:token', baboon.middleware.account.activate);
    app.use(baboon.middleware.session.initSession);
    app.use('/api/auth/login', baboon.middleware.auth.login);
    app.use('/api/auth/logout', baboon.middleware.auth.logout);
    app.use('/api/', baboon.transport.processRequest);
    app.use(baboon.middleware.session.checkActivitySession);
    app.use('/', routes);
    app.use(baboon.errorHandler);

    // Overwrite express app listen
    app.listen = function(){
        var server = baboon.getServer(app);
        return server.listen.apply(server, arguments);
    };
};
<commit_msg>feat: prepare expressjs plugins for next release (fix deprecated messages)<commit_after>'use strict';

var express = require('express');
var cookieParser = require('cookie-parser');
var session = require('express-session');
var bodyParser = require('body-parser');
var compress = require('compression');
var log4js = require('log4js');
var path = require('path');
var oneMonth = 2592000000;

/**
 * Config expressJs stack
 *
 * @param {!Object} app
 * @param {Array.<string>} routes
 * @param {!Object} baboon
 * @return {Object}
 */
module.exports = function (app, routes, baboon) {

    var loggers = baboon.loggers;
    var config = baboon.config;
    var rootPath = config.path.root;

    app.engine('html', require('ejs').renderFile);
    app.set('view engine', 'html');

    if (app.get('env') === 'development') {

        // Disable caching of scripts for easier testing
        app.use(function noCache (req, res, next) {

            if (req.url.indexOf('/app/') === 0 || req.url.indexOf('/common/') === 0 ||
                req.url.indexOf('/api/') === 0 || req.url.indexOf('/assets/') === 0) {

                res.header('Cache-Control', 'no-cache, no-store, must-revalidate');
                res.header('Pragma', 'no-cache');
                res.header('Expires', 0);
            }
            next();
        });

        // Logger in development mode
        app.use(log4js.connectLogger(loggers.express, {level: 'auto', format: ':status :method :url'}));

        // Static path for files and partial views
        app.use(express.static(path.join(rootPath, '.tmp')));
        app.use(express.static(path.join(rootPath, 'client')));

        // Path for application views
        app.set('views', rootPath + '/.tmp/views');
    }
    else if (app.get('env') === 'production') {

        // Enable compress
        app.use(compress());

        // Disable caching for rest api
        app.use(function noCache (req, res, next) {

            if (req.url.indexOf('/api/') === 0) {
                res.header('Cache-Control', 'no-cache, no-store, must-revalidate');
                res.header('Pragma', 'no-cache');
                res.header('Expires', 0);
            }
            next();
        });

        // Logger in production mode
        app.use(log4js.connectLogger(loggers.express, {level: 'auto'}));

        // Path for favicon with cache
        //app.use(express.favicon(path.join(rootPath, '.dist', 'public', 'favicon.ico'), {maxAge:oneMonth}));

        // Static path for files with cache
        app.use(express.static(path.join(rootPath, '.dist', 'public'), {maxAge: oneMonth}));

        // Static path for partial views without cache
        app.use(express.static(path.join(rootPath, '.dist', 'views', 'partials')));

        // Path for application views
        app.set('views', rootPath + '/.dist/views');
    }

    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({extended: false}));
    app.use(cookieParser());
    app.use(session({
        store: baboon.session.getSessionStore(),
        key: config.session.key,
        secret: config.session.secret,
        cookie: {expires: false},
        resave: false,
        saveUninitialized: true
    }));

    // Enable livereload
    if (app.get('env') === 'development' && config.livereload) {
        app.use(require('connect-livereload')());
    }

    app.use('/account/activate/:token', baboon.middleware.account.activate);
    app.use(baboon.middleware.session.initSession);
    app.use('/api/auth/login', baboon.middleware.auth.login);
    app.use('/api/auth/logout', baboon.middleware.auth.logout);
    app.use('/api/', baboon.transport.processRequest);
    app.use(baboon.middleware.session.checkActivitySession);
    app.use('/', routes);
    app.use(baboon.errorHandler);

    // Overwrite express app listen
    app.listen = function () {
        var server = baboon.getServer(app);
        return server.listen.apply(server, arguments);
    };
};
<|endoftext|>


<commit_before>const configParser = require('./index');
const massage = require('./massage');
const migration = require('./migration');
const npm = require('../manager/npm/registry');

module.exports = {
  resolveConfigPresets,
  replaceArgs,
  parsePreset,
  getPreset,
};

async function resolveConfigPresets(inputConfig, existingPresets = []) {
  logger.trace(
    { config: inputConfig, existingPresets },
    'resolveConfigPresets'
  );
  let config = {};
  // First, merge all the preset configs from left to right
  if (inputConfig.extends && inputConfig.extends.length) {
    if (existingPresets.length === 0) {
      logger.info('Found presets');
    }
    for (const preset of inputConfig.extends) {
      // istanbul ignore if
      if (existingPresets.indexOf(preset) !== -1) {
        logger.warn(`Already seen preset ${preset} in ${existingPresets}`);
      } else {
        logger.trace(`Resolving preset "${preset}"`);
        const presetConfig = await resolveConfigPresets(
          await getPreset(preset),
          existingPresets.concat([preset])
        );
        config = configParser.mergeChildConfig(config, presetConfig);
      }
    }
  }
  logger.trace({ config }, `Post-preset resolve config`);
  // Now assign "regular" config on top
  config = configParser.mergeChildConfig(config, inputConfig);
  delete config.extends;
  logger.trace({ config }, `Post-merge resolve config`);
  for (const [key, val] of Object.entries(config)) {
    const ignoredKeys = ['content'];
    if (isObject(val) && ignoredKeys.indexOf(key) === -1) {
      // Resolve nested objects
      logger.trace(`Resolving object "${key}"`);
      config[key] = await resolveConfigPresets(val, existingPresets);
    } else if (Array.isArray(val)) {
      // Resolve nested objects inside arrays
      config[key] = [];
      for (const element of val) {
        if (isObject(element)) {
          config[key].push(
            await resolveConfigPresets(element, existingPresets)
          );
        } else {
          config[key].push(element);
        }
      }
    }
  }
  logger.trace({ config: inputConfig }, 'Input config');
  logger.trace({ config }, 'Resolved config');
  return config;
}

function replaceArgs(obj, argMapping) {
  if (typeof obj === 'string') {
    let returnStr = obj;
    for (const [arg, argVal] of Object.entries(argMapping)) {
      const re = new RegExp(`{{${arg}}}`, 'g');
      returnStr = returnStr.replace(re, argVal);
    }
    return returnStr;
  }
  if (isObject(obj)) {
    const returnObj = {};
    for (const [key, val] of Object.entries(obj)) {
      returnObj[key] = replaceArgs(val, argMapping);
    }
    return returnObj;
  }
  if (Array.isArray(obj)) {
    const returnArray = [];
    for (const item of obj) {
      returnArray.push(replaceArgs(item, argMapping));
    }
    return returnArray;
  }
  return obj;
}

function parsePreset(input) {
  let str = input;
  let packageName;
  let presetName;
  let params;
  if (str.includes('(')) {
    params = str
      .slice(str.indexOf('(') + 1, -1)
      .split(',')
      .map(elem => elem.trim());
    str = str.slice(0, str.indexOf('('));
  }
  if (str[0] === ':') {
    // default namespace
    packageName = 'renovate-config-default';
    presetName = str.slice(1);
  } else if (str[0] === '@') {
    // scoped namespace
    [, packageName] = str.match(/(@.*?)(:|$)/);
    str = str.slice(packageName.length);
    if (!packageName.includes('/')) {
      packageName += '/renovate-config';
    }
    if (str === '') {
      presetName = 'default';
    } else {
      presetName = str.slice(1);
    }
  } else {
    // non-scoped namespace
    [, packageName] = str.match(/(.*?)(:|$)/);
    presetName = str.slice(packageName.length + 1);
    if (packageName.indexOf('renovate-config-') !== 0) {
      packageName = `renovate-config-${packageName}`;
    }
    if (presetName === '') {
      presetName = 'default';
    }
  }
  return { packageName, presetName, params };
}

async function getPreset(preset) {
  logger.trace(`getPreset(${preset})`);
  const { packageName, presetName, params } = parsePreset(preset);
  let presetConfig;
  try {
    const dep = await npm.getDependency(packageName);
    if (!dep) {
      logger.warn(`Failed to look up preset packageName ${packageName}`);
      return {};
    }
    if (!dep['renovate-config']) {
      logger.warn(`Package ${packageName} has no renovate-config`);
      return {};
    }
    presetConfig = dep['renovate-config'][presetName];
  } catch (err) {
    logger.warn({ err }, `Failed to look up package ${packageName}`);
  }
  if (!presetConfig) {
    logger.warn(`Cannot find preset ${preset}`);
    return {};
  }
  logger.debug(`Found preset ${preset}`);
  logger.trace({ presetConfig });
  if (params) {
    const argMapping = {};
    for (const [index, value] of params.entries()) {
      argMapping[`arg${index}`] = value;
    }
    presetConfig = replaceArgs(presetConfig, argMapping);
  }
  logger.trace({ presetConfig }, `Applied params to preset ${preset}`);
  const presetKeys = Object.keys(presetConfig);
  if (
    presetKeys.length === 2 &&
    presetKeys.includes('description') &&
    presetKeys.includes('extends')
  ) {
    // preset is just a collection of other presets
    delete presetConfig.description;
  }
  const packageListKeys = [
    'description',
    'packageNames',
    'excludePackageNames',
    'packagePatterns',
    'excludePackagePatterns',
  ];
  if (presetKeys.every(key => packageListKeys.includes(key))) {
    delete presetConfig.description;
  }
  const { migratedConfig } = migration.migrateConfig(presetConfig);
  return massage.massageConfig(migratedConfig);
}

function isObject(obj) {
  return Object.prototype.toString.call(obj) === '[object Object]';
}
<commit_msg>refactor: add unstablePattern to list of ignored preset keys<commit_after>const configParser = require('./index');
const massage = require('./massage');
const migration = require('./migration');
const npm = require('../manager/npm/registry');

module.exports = {
  resolveConfigPresets,
  replaceArgs,
  parsePreset,
  getPreset,
};

async function resolveConfigPresets(inputConfig, existingPresets = []) {
  logger.trace(
    { config: inputConfig, existingPresets },
    'resolveConfigPresets'
  );
  let config = {};
  // First, merge all the preset configs from left to right
  if (inputConfig.extends && inputConfig.extends.length) {
    if (existingPresets.length === 0) {
      logger.info('Found presets');
    }
    for (const preset of inputConfig.extends) {
      // istanbul ignore if
      if (existingPresets.indexOf(preset) !== -1) {
        logger.warn(`Already seen preset ${preset} in ${existingPresets}`);
      } else {
        logger.trace(`Resolving preset "${preset}"`);
        const presetConfig = await resolveConfigPresets(
          await getPreset(preset),
          existingPresets.concat([preset])
        );
        config = configParser.mergeChildConfig(config, presetConfig);
      }
    }
  }
  logger.trace({ config }, `Post-preset resolve config`);
  // Now assign "regular" config on top
  config = configParser.mergeChildConfig(config, inputConfig);
  delete config.extends;
  logger.trace({ config }, `Post-merge resolve config`);
  for (const [key, val] of Object.entries(config)) {
    const ignoredKeys = ['content'];
    if (isObject(val) && ignoredKeys.indexOf(key) === -1) {
      // Resolve nested objects
      logger.trace(`Resolving object "${key}"`);
      config[key] = await resolveConfigPresets(val, existingPresets);
    } else if (Array.isArray(val)) {
      // Resolve nested objects inside arrays
      config[key] = [];
      for (const element of val) {
        if (isObject(element)) {
          config[key].push(
            await resolveConfigPresets(element, existingPresets)
          );
        } else {
          config[key].push(element);
        }
      }
    }
  }
  logger.trace({ config: inputConfig }, 'Input config');
  logger.trace({ config }, 'Resolved config');
  return config;
}

function replaceArgs(obj, argMapping) {
  if (typeof obj === 'string') {
    let returnStr = obj;
    for (const [arg, argVal] of Object.entries(argMapping)) {
      const re = new RegExp(`{{${arg}}}`, 'g');
      returnStr = returnStr.replace(re, argVal);
    }
    return returnStr;
  }
  if (isObject(obj)) {
    const returnObj = {};
    for (const [key, val] of Object.entries(obj)) {
      returnObj[key] = replaceArgs(val, argMapping);
    }
    return returnObj;
  }
  if (Array.isArray(obj)) {
    const returnArray = [];
    for (const item of obj) {
      returnArray.push(replaceArgs(item, argMapping));
    }
    return returnArray;
  }
  return obj;
}

function parsePreset(input) {
  let str = input;
  let packageName;
  let presetName;
  let params;
  if (str.includes('(')) {
    params = str
      .slice(str.indexOf('(') + 1, -1)
      .split(',')
      .map(elem => elem.trim());
    str = str.slice(0, str.indexOf('('));
  }
  if (str[0] === ':') {
    // default namespace
    packageName = 'renovate-config-default';
    presetName = str.slice(1);
  } else if (str[0] === '@') {
    // scoped namespace
    [, packageName] = str.match(/(@.*?)(:|$)/);
    str = str.slice(packageName.length);
    if (!packageName.includes('/')) {
      packageName += '/renovate-config';
    }
    if (str === '') {
      presetName = 'default';
    } else {
      presetName = str.slice(1);
    }
  } else {
    // non-scoped namespace
    [, packageName] = str.match(/(.*?)(:|$)/);
    presetName = str.slice(packageName.length + 1);
    if (packageName.indexOf('renovate-config-') !== 0) {
      packageName = `renovate-config-${packageName}`;
    }
    if (presetName === '') {
      presetName = 'default';
    }
  }
  return { packageName, presetName, params };
}

async function getPreset(preset) {
  logger.trace(`getPreset(${preset})`);
  const { packageName, presetName, params } = parsePreset(preset);
  let presetConfig;
  try {
    const dep = await npm.getDependency(packageName);
    if (!dep) {
      logger.warn(`Failed to look up preset packageName ${packageName}`);
      return {};
    }
    if (!dep['renovate-config']) {
      logger.warn(`Package ${packageName} has no renovate-config`);
      return {};
    }
    presetConfig = dep['renovate-config'][presetName];
  } catch (err) {
    logger.warn({ err }, `Failed to look up package ${packageName}`);
  }
  if (!presetConfig) {
    logger.warn(`Cannot find preset ${preset}`);
    return {};
  }
  logger.debug(`Found preset ${preset}`);
  logger.trace({ presetConfig });
  if (params) {
    const argMapping = {};
    for (const [index, value] of params.entries()) {
      argMapping[`arg${index}`] = value;
    }
    presetConfig = replaceArgs(presetConfig, argMapping);
  }
  logger.trace({ presetConfig }, `Applied params to preset ${preset}`);
  const presetKeys = Object.keys(presetConfig);
  if (
    presetKeys.length === 2 &&
    presetKeys.includes('description') &&
    presetKeys.includes('extends')
  ) {
    // preset is just a collection of other presets
    delete presetConfig.description;
  }
  const packageListKeys = [
    'description',
    'packageNames',
    'excludePackageNames',
    'packagePatterns',
    'excludePackagePatterns',
    'unstablePattern',
  ];
  if (presetKeys.every(key => packageListKeys.includes(key))) {
    delete presetConfig.description;
  }
  const { migratedConfig } = migration.migrateConfig(presetConfig);
  return massage.massageConfig(migratedConfig);
}

function isObject(obj) {
  return Object.prototype.toString.call(obj) === '[object Object]';
}
<|endoftext|>


<commit_before>'use strict';

var compact    = require('es5-ext/lib/Array/prototype/compact')
  , i          = require('es5-ext/lib/Function/i')
  , partial    = require('es5-ext/lib/Function/prototype/partial')
  , mapToArray = require('es5-ext/lib/Object/map-to-array')
  , callable   = require('es5-ext/lib/Object/valid-callable')
  , value      = require('es5-ext/lib/Object/valid-value')
  , numPad     = require('es5-ext/lib/Number/prototype/pad')
  , pad        = require('es5-ext/lib/String/prototype/pad')
  , repeat     = require('es5-ext/lib/String/prototype/repeat')
  , memoize    = require('memoizee')
  , deferred   = require('deferred')
  , clc        = require('cli-color')
  , readFile   = require('next/lib/fs/read-file')
  , normalize  = require('./_normalize-source')

  , isArray = Array.isArray
  , ceil = Math.ceil, max = Math.max, min = Math.min
  , log = process.stdout.write.bind(process.stdout)
  , nextTick = process.nextTick
  , reportFile, isStatus

  , eolRe = /(?:\r\n|[\n\r\u2028\u2029])/

  , Reporter;

Reporter = function () {};
Reporter.prototype = {
	parsed: 0,
	failed: 0,
	errors: 0,
	all: 0,
	init: function (data, options) {
		var waiting, status, isStatus;
		if (options.progress) {
			waiting = [];
			status = memoize(function () {  nextTick(function () {
				status.clear();
				this.status();
				isStatus = true;
			}.bind(this)); }.bind(this));

			data.on('change', function (event) {
				this.calc(event.report);
				if (event.report.length) {
					waiting.push(this.normalize(event.report)(function (report) {
						if (report) {
							if (isStatus) {
								log(clc.bol(-3, true));
								isStatus = false;
							}
							this.file(event.name, report);
							status();
						}
					}.bind(this)));
				} else {
					status();
				}
			}.bind(this));
			return data(function () {
				return deferred.map(waiting);
			}.bind(this));
		} else {
			return data(function (data) {
				return deferred.map(mapToArray(data, function (report, name) {
					this.calc(report);
					if (report.length) {
						return this.normalize(report)(function (report) {
							if (report) {
								this.file(name, report);
							}
						}.bind(this));
					}
				}, this));
			}.bind(this))(this.status.bind(this));
		}
	},
	emap: {
		report: {
			name: clc.bold,
			options: clc.xterm(239),

			count: clc.xterm(232),
			messageErr: clc.yellow,
			lineNum: clc.xterm(240),
			code: clc.xterm(250),
			at: clc.xterm(250).bgXterm(235),
			messageOK: clc.green
		},
		status: {
			OK: i,
			failed: clc.bold.red,
			errors: clc.red
		}
	},
	options: function (options) {
		var escape = this.emap.report.options;
		options = compact.call(mapToArray(options, function (value, name) {
			if (value === true) {
				return name;
			} else if (value === false) {
				return null;
			} else {
				return name + '=' + value;
			}
		}));
		return escape(options.length ? options.join(' ') : 'no options');
	},
	calc: function (report) {
		++this.all;
		if (report.length) {
			++this.failed;
			this.errors += report.length;
		}
	},
	directOutput: function (report, name) {
		this.calc(report);
		return this.normalize(report)(function (report) {
			if (report) {
				this.file(name, report);
			}
		}.bind(this));
	},
	normalize: function (report) {
		if (report.length) {
			if (!report.src) {
				return readFile(report.path)(function (src) {
					report.src = normalize(String(src));
					return this.normalize(report);
				}.bind(this), function (err) {
					if (!err.code || (err.code === 'EMFILE')) {
						return err;
					}
					--this.all;
					if (report.length) {
						--this.failed;
						this.errors -= report.length;
					}
				}.bind(this));
			} else {
				report.srcLines = report.src.split(eolRe);
			}
		}
		return deferred(report);
	},
	file: function (name, report) {
		var emap = this.emap.report, str = '', countPad, srcPad, indent;
		str += (name ? (emap.name(name) + '\n') : '') + this.options(report.options);
		if (report.length) {
			str += '\n';
			countPad = partial.call(pad, ' ', String(report.length).length);
			srcPad = partial.call(numPad, max(String(report.srcLines.length).length,
				String(report.length).length + 1));
			indent = repeat.call(' ', isNaN(report.options.indent) ? 4 :
				max(min(Number(report.options.indent), 16), 1));

			report.forEach(function (data, index) {
				var line = report.srcLines[data.line - 1].replace(/\t/g, indent)
				  , at = data.character - 1;

				str += emap.count('#' + countPad.call(index + 1)) + ' '
					+ emap.messageErr(data.message) + '\n'
					+ emap.lineNum(srcPad.call(data.line)) + ' '
					+ emap.code(line.slice(0, at)) + emap.at(line[at])
					+ emap.code(line.slice(at + 1)) + '\n';
			});
		} else {
			str += ' ' + emap.messageOK('OK') + '\n';
		}
		log(str + '\n');
	},
	status: function () {
		var str = '', ok, emap = this.emap.status;
		if (this.single) {
			if (this.failed) {
				str += emap.failed(this.errors + ' Errors');
			} else {
				str += emap.OK("All OK\n\n");
			}
		} else {
			ok = (this.all - this.failed);
			str += emap.OK(ok + ' OK [' + ((ok / this.all) * 100).toFixed(2) + '%]')
				+ '  ' + emap.failed(this.failed + ' Failed') + ' '
				+ emap.errors('[' + this.errors + ' Errors]');
		}
		log(str + '\n\n');
	}
};

module.exports = function (report, options) {
	var reporter;
	callable(report) && value(options);

	reporter = new Reporter();
	return reporter.init(report, options);
};
<commit_msg>Better clear status logic<commit_after>'use strict';

var compact    = require('es5-ext/lib/Array/prototype/compact')
  , i          = require('es5-ext/lib/Function/i')
  , partial    = require('es5-ext/lib/Function/prototype/partial')
  , mapToArray = require('es5-ext/lib/Object/map-to-array')
  , callable   = require('es5-ext/lib/Object/valid-callable')
  , value      = require('es5-ext/lib/Object/valid-value')
  , numPad     = require('es5-ext/lib/Number/prototype/pad')
  , pad        = require('es5-ext/lib/String/prototype/pad')
  , repeat     = require('es5-ext/lib/String/prototype/repeat')
  , memoize    = require('memoizee')
  , deferred   = require('deferred')
  , clc        = require('cli-color')
  , readFile   = require('next/lib/fs/read-file')
  , normalize  = require('./_normalize-source')

  , isArray = Array.isArray
  , ceil = Math.ceil, max = Math.max, min = Math.min
  , log = process.stdout.write.bind(process.stdout)
  , nextTick = process.nextTick
  , reportFile, isStatus

  , eolRe = /(?:\r\n|[\n\r\u2028\u2029])/

  , Reporter;

Reporter = function () {};
Reporter.prototype = {
	parsed: 0,
	failed: 0,
	errors: 0,
	all: 0,
	init: function (data, options) {
		var waiting, status, isStatus, clearStatus;
		if (options.progress) {
			waiting = [];
			status = memoize(function () {  nextTick(function () {
				status.clear();
				clearStatus();
				this.status();
				isStatus = true;
			}.bind(this)); }.bind(this));

			clearStatus = function () {
				if (isStatus) {
					log(clc.bol(-2, true));
					isStatus = false;
				}
			};

			data.on('change', function (event) {
				this.calc(event.report);
				if (event.report.length) {
					waiting.push(this.normalize(event.report)(function (report) {
						if (report) {
							clearStatus();
							this.file(event.name, report);
							status();
						}
					}.bind(this)));
				} else {
					status();
				}
			}.bind(this));
			return data(function () {
				return deferred.map(waiting);
			}.bind(this));
		} else {
			return data(function (data) {
				return deferred.map(mapToArray(data, function (report, name) {
					this.calc(report);
					if (report.length) {
						return this.normalize(report)(function (report) {
							if (report) {
								this.file(name, report);
							}
						}.bind(this));
					}
				}, this));
			}.bind(this))(this.status.bind(this));
		}
	},
	emap: {
		report: {
			name: clc.bold,
			options: clc.xterm(239),

			count: clc.xterm(232),
			messageErr: clc.yellow,
			lineNum: clc.xterm(240),
			code: clc.xterm(250),
			at: clc.xterm(250).bgXterm(235),
			messageOK: clc.green
		},
		status: {
			OK: i,
			failed: clc.bold.red,
			errors: clc.red
		}
	},
	options: function (options) {
		var escape = this.emap.report.options;
		options = compact.call(mapToArray(options, function (value, name) {
			if (value === true) {
				return name;
			} else if (value === false) {
				return null;
			} else {
				return name + '=' + value;
			}
		}));
		return escape(options.length ? options.join(' ') : 'no options');
	},
	calc: function (report) {
		++this.all;
		if (report.length) {
			++this.failed;
			this.errors += report.length;
		}
	},
	directOutput: function (report, name) {
		this.calc(report);
		return this.normalize(report)(function (report) {
			if (report) {
				this.file(name, report);
			}
		}.bind(this));
	},
	normalize: function (report) {
		if (report.length) {
			if (!report.src) {
				return readFile(report.path)(function (src) {
					report.src = normalize(String(src));
					return this.normalize(report);
				}.bind(this), function (err) {
					if (!err.code || (err.code === 'EMFILE')) {
						return err;
					}
					--this.all;
					if (report.length) {
						--this.failed;
						this.errors -= report.length;
					}
				}.bind(this));
			} else {
				report.srcLines = report.src.split(eolRe);
			}
		}
		return deferred(report);
	},
	file: function (name, report) {
		var emap = this.emap.report, str = '', countPad, srcPad, indent;
		str += (name ? (emap.name(name) + '\n') : '') + this.options(report.options);
		if (report.length) {
			str += '\n';
			countPad = partial.call(pad, ' ', String(report.length).length);
			srcPad = partial.call(numPad, max(String(report.srcLines.length).length,
				String(report.length).length + 1));
			indent = repeat.call(' ', isNaN(report.options.indent) ? 4 :
				max(min(Number(report.options.indent), 16), 1));

			report.forEach(function (data, index) {
				var line = report.srcLines[data.line - 1].replace(/\t/g, indent)
				  , at = data.character - 1;

				str += emap.count('#' + countPad.call(index + 1)) + ' '
					+ emap.messageErr(data.message) + '\n'
					+ emap.lineNum(srcPad.call(data.line)) + ' '
					+ emap.code(line.slice(0, at)) + emap.at(line[at])
					+ emap.code(line.slice(at + 1)) + '\n';
			});
		} else {
			str += ' ' + emap.messageOK('OK') + '\n';
		}
		log(str + '\n');
	},
	status: function () {
		var str = '', ok, emap = this.emap.status;
		if (this.single) {
			if (this.failed) {
				str += emap.failed(this.errors + ' Errors');
			} else {
				str += emap.OK("All OK\n\n");
			}
		} else {
			ok = (this.all - this.failed);
			str += emap.OK(ok + ' OK [' + ((ok / this.all) * 100).toFixed(2) + '%]')
				+ '  ' + emap.failed(this.failed + ' Failed') + ' '
				+ emap.errors('[' + this.errors + ' Errors]');
		}
		log(str + '\n\n');
	}
};

module.exports = function (report, options) {
	var reporter;
	callable(report) && value(options);

	reporter = new Reporter();
	return reporter.init(report, options);
};
<|endoftext|>


<commit_before>var Analytics = function(){};
var _getStatus = function(){};
var _updateStatus = function(){};

Analytics.prototype.getStatus = _getStatus;
Analytics.prototype.updateStatus = _updateStatus;
module.exports = Analytics;<commit_msg>Analytics module developed<commit_after>var _getStatus = function() {
	var threads = this.threads;
	if (this.threads === undefined) return;
	var i = 0;
	while (i < threads.length) {

		//THREAD POSITIONS
		var t = threads[i++];
		var val = Math.floor((t.position - t.start) * 10000 / (t.end - t.start)) / 100;
		val = val.toString();
		this.threadCompleted.push(val);

		//ACTIVE THREADS
		if (t.connection == 'open') {
			this.openConnections++;
		}

		//COMPLETED THREADS
		if (t.connection == 'closed') {
			this.closedConnections++;
		}

		//FAILED THREADS
		if (t.connection == 'failed') {
			this.failedConnections++;
		}

		//DOWNLOAD SIZE
		this.downloadSize += (t.end - t.start + 1);

		//DATA RECEIVED
		this.dataReceived += (t.position - t.start + 1);

	}

	this.downloadSize--;
	this.dataReceived--;
};

var Analytics = function(options) {

	this.threads = options.threads;

	if (this.threads instanceof Array && this.threads.length > 0) {
		this.fileSize = this.threads[this.threads.length - 1].end;
		this.blockSize = this.fileSize / this.threads.length;
	} else {
		this.fileSize = 0;
		this.blockSize = 0;
	}
	//console.log(this);

	this.interval = options.interval || 1000;

	this.downloadSize = 0;
	this.dataReceived = 0;

	this.downloadSpeed = 0;

	this.completed = 0;
	this.threadCompleted = [];

	this.remaining = 0;
	this.elapsedTime = 0;
	this.estimatedTime = 0;
	this.openConnections = 0;
	this.closedConnections = 0;
	this.failedConnections = 0;

	_getStatus.call(this);
};

var _tick = function() {
	this.elapsedTime += this.interval;
};

Analytics.prototype.tick = _tick;
module.exports = Analytics;<|endoftext|>


<commit_before>var createController = require("../../libs/controllers").createController;

var ProjectController = createController({
  Projects: null,

  setup: function(req, res) {
    this.Projects = this.repositories.Projects;
  },

  init: function(){
    // initializing a reactive method
    var that = this;
    this.sockets.createReactiveMethod('callProjectListController', function(parameters, callback){
      that.list(null, null, function(projects){
        callback(projects);
      });

    });
  },

  // /projects
  // reactiveMethodCallback is there to make the method usable via reactive methods
  list: function(req, res, reactiveMethodCallback) {
    var that = this;

    this.Projects.getAllByDate(function(projects) {
      // if called from reactive method
      if (res == undefined) {
        reactiveMethodCallback(projects);
      }

      res.render("projects/list", {
        title: "List of projects",
        projects: projects
      });
    });
  },

  // /projects/new
  new: function(req, res) {
    res.render("projects/edit", { title: "Create a new project" });
  },

  // /projects/edit/:project_code
  editGet: function(req, res) {
    var that = this;

    this.Projects.getByCode(req.params.project_code, function(project) {
      if (project) {
        res.render("projects/edit", {
          title: "Editing project: " + project.name,
          project: project
        });

      } else {
        that.messages.addMessage(req, "error", "Unable to find project.");
        res.redirect("/projects");
      }
    });
  },

  // /projects/edit/:project_code
  editPost: function(req, res) {
    var that = this;

    this.Projects.save(req.body.project, function(project, errors) {
      if (errors) {
        that.validator.addErrorsToMessages(req, errors);
      } else {
        that.messages.addMessage(req, "success", "Saved with success.");
      }

      res.render("projects/edit", {
        title: "Editing project: " + project.name,
        project: project
      });
    });
  },

  // /projects/delete/:object_id
  delete: function(req, res) {
    var that = this;

    this.Projects.delete(req.params.object_id, function(success, project) {
      if (success) {
        that.messages.addMessage(req, "success", "Project \"" + project.name + "\" has been deleted with success.");
      } else {
        that.messages.addMessage(req, "error", "Unable to find project.");
      }
      res.redirect("/projects");
    });
  }

});

module.exports = ProjectController;<commit_msg>short<commit_after>var createController = require("../../libs/controllers").createController;

var ProjectController = createController({
  Projects: null,

  setup: function(req, res) {
    this.Projects = this.repositories.Projects;
  },

  init: function(){
    // initializing a reactive method
    var that = this;
    this.sockets.createReactiveMethod('callProjectListController', function(parameters, callback){
      that.list(null, null, function(projects){
        callback(projects);
      });

    });
  },

  // /projects
  // reactiveMethodCallback is there to make the method usable via reactive methods
  list: function(req, res, reactiveMethodCallback) {
    var that = this;

    this.Projects.getAllByDate(function(projects) {
      // if called from reactive method
      if (!res) {
        reactiveMethodCallback(projects);
      }

      res.render("projects/list", {
        title: "List of projects",
        projects: projects
      });
    });
  },

  // /projects/new
  new: function(req, res) {
    res.render("projects/edit", { title: "Create a new project" });
  },

  // /projects/edit/:project_code
  editGet: function(req, res) {
    var that = this;

    this.Projects.getByCode(req.params.project_code, function(project) {
      if (project) {
        res.render("projects/edit", {
          title: "Editing project: " + project.name,
          project: project
        });

      } else {
        that.messages.addMessage(req, "error", "Unable to find project.");
        res.redirect("/projects");
      }
    });
  },

  // /projects/edit/:project_code
  editPost: function(req, res) {
    var that = this;

    this.Projects.save(req.body.project, function(project, errors) {
      if (errors) {
        that.validator.addErrorsToMessages(req, errors);
      } else {
        that.messages.addMessage(req, "success", "Saved with success.");
      }

      res.render("projects/edit", {
        title: "Editing project: " + project.name,
        project: project
      });
    });
  },

  // /projects/delete/:object_id
  delete: function(req, res) {
    var that = this;

    this.Projects.delete(req.params.object_id, function(success, project) {
      if (success) {
        that.messages.addMessage(req, "success", "Project \"" + project.name + "\" has been deleted with success.");
      } else {
        that.messages.addMessage(req, "error", "Unable to find project.");
      }
      res.redirect("/projects");
    });
  }

});

module.exports = ProjectController;<|endoftext|>


<commit_before>#!/usr/bin/env node
var revisionRange = process.argv[2];

if (!revisionRange) {
  console.log('Must supply an argument that is a git revision range like `origin/master..origin/develop`. See here for many variations: ');
  console.log('https://www.kernel.org/pub/software/scm/git/docs/gitrevisions.html');
  process.exit(1);
}
var exec = require('child_process').execSync;
var dir = process.cwd();
var remote = 'origin';
var gitUrlBase = sh('cd ' + dir + ' && git config --get remote.' + remote + '.url')
.replace(':', '/')
.replace(/^git@/, 'http://')
.replace('.git', '')
.replace('\n', '');

function sh(cmd) {
  return exec(cmd, {encoding: 'utf-8'});
}

function commitFormat() {
  return '[%h](' + gitUrlBase + '/commit/' + '%h)';
}

var x = sh('cd ' + dir + ' && git shortlog ' + revisionRange + ' --numbered --no-merges --format="- ' + commitFormat() + ' %s"');

x = x.replace(/^[a-z|A-Z].*:$/mg, '## $&  ');
x = x.replace(/^\s*\-/mg, '-');
console.log(x);
<commit_msg>permission change<commit_after>#!/usr/bin/env node
var revisionRange = process.argv[2];

if (!revisionRange) {
  console.log('Must supply an argument that is a git revision range like `origin/master..origin/develop`. See here for many variations: ');
  console.log('https://www.kernel.org/pub/software/scm/git/docs/gitrevisions.html');
  process.exit(1);
}
var exec = require('child_process').execSync;
var dir = process.cwd();
var remote = 'origin';
var gitUrlBase = sh('cd ' + dir + ' && git config --get remote.' + remote + '.url')
.replace(':', '/')
.replace(/^git@/, 'http://')
.replace('.git', '')
.replace('\n', '');

function sh(cmd) {
  return exec(cmd, {encoding: 'utf-8'});
}

function commitFormat() {
  return '[%h](' + gitUrlBase + '/commit/' + '%h)';
}

var x = sh('cd ' + dir + ' && git shortlog ' + revisionRange + ' --numbered --no-merges --format="- ' + commitFormat() + ' %s"');

x = x.replace(/^[a-z|A-Z].*:$/mg, '## $&  ');
x = x.replace(/^\s*\-/mg, '-');
console.log(x);
<|endoftext|>


<commit_before>// ==UserScript==
// @name         Giveaway Killer (a.k.a. Giveaway Enhancer)
// @namespace    https://github.com/gekkedev/giveawayHelperEnhancer
// @version      0.6
// @description  Enhances Steam key giveaways sites by lots of useful features
// @author       gekkedev
// @match        https?://*.marvelousga.com/*
// @match        https?://*.dupedornot.com/*
// @match        https?://*.indiegala.com/*
// @match        https?://*.dogebundle.com/*
// @match        https?://*.giveaway.su/*
// @match        https?://*.steamgifts.com/*
// @match        https?://*.simplo.gg/*
// @match        https?://*.giveawayhopper.com/*
// @match        https?://*.cubicbundle.com/*
// @match        https?://*.treasuregiveaways.com/*
// @match        https?://*.goldengiveaways.org/*
// @match        https?://*.bananagiveaway.com/*
// @match        https?://*.gamehag.com/*
// @match        https?://*.steamcommunity.com/openid/login*
// @match        https?://*.steamcommunity.com/oauth/login*
// @grant        none
// @updateURL https://raw.githubusercontent.com/gekkedev/giveawayHelperEnhancer/master/giveawayHelperEnhancer.user.js
// @downloadURL https://raw.githubusercontent.com/gekkedev/giveawayHelperEnhancer/master/giveawayHelperEnhancer.user.js
// @require https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/blueimp-md5/2.10.0/js/md5.min.js
// @run-at document-end
// ==/UserScript==

(function() {
    J = jQuery.noConflict(true); //to prevent i.e. broken buttons when a site uses jquery too
    /**
    * Determine what to do for this page based on what's defined in the "config" variable
    *
    * 		hostname: String
    *			The hostname of the site we're setting the config for. Must be the same as what's defined
    *			as @match in the metadata block above.
    *
    *		googleads: Boolean
    * 			This tells us if the site we visit has ads (this refers to banners of any size). We'll then trigger the removal function for that.
    *
    *		autologin: String
    * 			This is a selector for a link which has to be visited in order to get forwarded to the login page.
    *
    *		clickables: Array of String
    * 			These are CSS Selectors which will be buttons to be clicked (in ascending order). Some may not be clickable but each click that doesn't need to be done is already a success.
    *
    */
    var removePopups = (function(){
        //console.log("removing popups!");
        removeElement("a[id^='popup']");
    });
    var fakeClickLinks = function() {
        J("form[action*='verify_click.php']").trigger("submit");
    };
    var solveGamehagUsernameCheck = function() {
        //"Kruemel" //some random user, just pick another one from the ranklist in case they block this user ;-)
    };
    scanForElement = function(ident) {
        //console.log("found " + ident + " " + jQuery(ident).length + " time(s)");console.log(jQuery(ident));
        if (J(ident).length >= 1) {
            return true;
        } else {
            //console.log('cannot find ' + ident);
            return false;
        }
    };
    hideElement = function(ident) {
        if (scanForElement(ident)) {
            J(ident).hide();
        }
    };
    removeElement = function(ident) {
        if (scanForElement(ident)) {
            J(ident).remove();
        }
    };
    var clickElement = function(ident) {
        if (scanForElement(ident)) {
            J(ident).click();
        }
    };
    var visitLink = (function(ident){
        window.location.replace(J(ident).attr("href"));
    });
    var removeGoogleAds = (function(){
        //console.log("removing google ads!");
        removeElement("ins[class*='adsbygoogle']");
        setTimeout(removeGoogleAds, 1000);
    });
  	var getEncapsedString = function(input, delimiter) {//just gets the first occurrence
        var startpos = input.indexOf(delimiter) + delimiter.length;
        var endpos = input.indexOf(delimiter, startpos);
        return input.substring(startpos, endpos)
    }
    var getRandomInt = function(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    };

    /*var autoJoinGroups = function() {
        //giveaway.su
        var groups = $("button[data-type='steam.group']");
        for (var i = 0; i < groups.length; i++) {
            var group = groups[i];
            $.get("https://steamcommunity.com/profiles/"+$("aside[data-steam-id]").data("steam-id")+"/groups/").always(function(jqXHR) {
                var regexp = new RegExp("href=\"javascript:leaveGroupPrompt\\('"+$(group).data("check")+"',", "g");
                if (jqXHR && regexp.test(jqXHR))
                    toggleActionButton(button);
                else
                    $(button).prop("disabled", false).find(".glyphicon.spin").removeClass("spin");
            });
            group.remove();
        }
    };*/
    var taskSkipper_1 = function() {//giveaway.su/varlick
        if ($("article .extension").length) {
            $("article .extension").addClass("installed");
            var timestamp = $("article .extension").data("timestamp");
            var csrf = $("article .extension").data("csrf");
            var extension = md5(timestamp+""+csrf);
            var secret = $("article .extension").data("secret");
            $.get(window.location.pathname+"?extension="+extension+"&timestamp="+timestamp+"&csrf="+csrf+(secret ? "&secret="+secret : ""), function(response) {
                $("article").replaceWith(response);
                //this is the extension version and should not depend on manual updates
                $("article").attr("extension-version", "chrome-3.4.9");//+chrome.runtime.getManifest().version);

                var button = $("#getKey a");
                var actions = 0;
                $("#actions [data-action-id]").each(function(i,el){
                    actions += parseInt($(el).data("action-id"));
                    if ($(el).find("button[data-type='steam.curator']").length) {
                        $(el).fadeOut();
                    } else if ($(el).find("button[data-type='youtube.subscribe']").length) {
                        $(el).fadeOut();
                    } else if ($(el).find("button[data-type='twitter.follow']").length) {
                        $(el).fadeOut();
                    } else if ($(el).find("button[data-type='steam.game.wishlist']").length) {
                        $(el).fadeOut();
                    } else if ($(el).find("td:contains('Invite')").length) {
                        $(el).fadeOut();
                    }
                });
                $(button).attr("href", $(button).attr("href")+"&actions="+md5(actions)).removeClass("disabled");
                $("#getKey").prepend("Giveaway Killer by gekkedev has skipped some tasks for you, because this site is trying to manipulate the Steam store and to get access over your private data. Key claiming does usually work when you have joined all the required groups. Please use the Giveaway Helper by Citrinate in order to join groups easier. Linking accounts happens at your own risk and is a possible reason of unwanted actions commited via your account (account theft, unwanted purchases, etc.) and is qualifying you for punishments regarding Steam T.O.S. violations.
");
            });
        }
    };
    var taskSkipper_2 = function() {//goldengiveaways
        var tasks = J("a[onclick*='task()']");
        if (tasks.length) {
            tasks.each(function(counter, element){
                J.get(element.href);
            });
            location.reload();
        } else {
            J(".giveaway-new").last().prepend("
Giveaway Killer by gekkedev has skipped some tasks for you, because this site is trying to manipulate the Steam store by asking you to follow specific curators. Doing such would qualify you for punishments regarding Steam T.O.S. violations.

");
        }
    };
    var taskSkipper_3 = function() {//bananagiveaway/bananatic
        //get only facebook and youtube; steam group, twitter, and point collection tasks might be different
        var tasks = J("li:contains('FB'):contains('Like'), li:contains('Share'):not(li:contains('Twitter'))");
        tasks = tasks.add(J("li:contains('YT'):contains('Check')"));
        //tasks = tasks.add(J("li:contains('Share'):contains('Twitter')"));

      	//dig deeper, but keep the task steps together
        tasks = tasks.find("div[class='buttons']");
        var successes = 0;
        J.ajaxSetup({async:false}); //not so smooth looking but easy
        if (tasks.length) {console.log(tasks);
            tasks.each(function(counter, task){
                J.get( //hit the first link...
                    getEncapsedString(J(J(task).find("button:not([disabled])")).attr("onclick"), "'"),
                    function() {//...and only continue with the second link on success...
                        //which will, on success, make sure we really solved the task
                        J.get(
                            getEncapsedString(J(J(task).find("button[disabled]")).attr("onclick"), "'"),
                            function(){
                                successes++;
                            }
                        );
                    }
                );
            });
            J.ajaxSetup({async:true});
            if (tasks.length == successes) {
                location.reload();
            } else {
                console.log("Could not solve all solveable tasks!");
            }
        } else {
            J("div[class='bottom'] span[class='status']").prepend("
Giveaway Killer by gekkedev has skipped some tasks for you, because this site is exploiting it's users by excessive ad banner usage plus making users fullfill specific tasks which generate a dozen times more revenue than what is offered to the user.

");
        }
    };

    //configuration

    var config = [
        {
            hostname: "marvelousga.com",
            ads: true,
            autologin: "a[href='?login']",
            //clickables: ["button[type=submit][name!=sg][id!=submit]"],
            //autosolve: "'div.collapse* button'"
            trigger: [removePopups, solveGamehagUsernameCheck] //fakeClickLinks
        },
        {
            hostname: "dupedornot.com",
            ads: true,
            autologin: "a[href='?login']",
            trigger: [removePopups]
        },
        {
            hostname: "dogebundle.com",
            ads: false,
            autologin: "a[href*='?page=login']"
        },
        {
            hostname: "indiegala.com",
            ads: true,
            clickables: ["button#close-socials"],
            custom: function() {J('#myModal-givform').modal('show');}
        },
        {
            hostname: "giveaway.su",
            ads: true,
            autologin: "a.steam-login",
            trigger: [taskSkipper_1],
            custom: function() {hideElement("ul.menu");}
        },
        {
            hostname: "simplo.gg",
            ads: true,
            autologin: "div.steam-button a",
            clickables: ["w-div[role=dialog] span"]
        },
        {
            hostname: "steamgifts.com",
            ads: true,
            autologin: "a.nav__sits"
        },
        {
            hostname: "chubbykeys.com",
            ads: false
        },
        {
            hostname: "giveawayhopper.com",
            ads: false,
            autologin: "a[href='?login']"
        },
        {
            hostname: "cubicbundle.com",
            ads: false,
            autologin: "a.no-login"
        },
        {
            hostname: "treasuregiveaways.com",
            clickables: ["input[onclick*=incr]"],
            custom: function() {J('form[action="?login"]').submit();}
        },
        {
            hostname: "goldengiveaways.org",
            ads: false,
            autologin: "a[href='/?login']",
            trigger: [taskSkipper_2]
        },
        {
            hostname: "bananagiveaway.com",
            ads: true,
            autologin: "a[href*='/steam/']",
            trigger: [taskSkipper_3]
        },
        {
            hostname: "gamehag.com",
            ads: true
        },
        {
            hostname: "steamcommunity.com",
            ads: false,
            clickables: ["input[type='submit']"]
        }
    ];

    //execution
    for(var i = 0; i < config.length; i++) {
        var site = config[i];

        if(document.location.hostname.split(".").splice(-2).join(".") == site.hostname) {
            if (site.ads) {
                removeGoogleAds();
                //removePopups();
                //add other ad services here if known
            }
            if (site.autologin !== undefined) {
                if (scanForElement(site.autologin)) {
                    console.log('performing autologin');
                    visitLink(site.autologin);
                }
            }
            if (site.clickables !== undefined) {
                if (site.clickables.length > 0) {
                    for(var j = 0; j < site.clickables.length; j++) {
                        if (J.isArray(site.clickables[j])) {
                            j = getRandomInt(0, site.clickables[j].length);
                            console.log("picking the entry " + j);
                        }
                        clickElement(site.clickables[j]);
                    }
                }
            }
            if (site.trigger !== undefined) {
                for(var j = 0; j < site.trigger.length; j++) {
                    site.trigger[j]();
                }
            }
            if (site.custom !== undefined) {
                site.custom();
            }
        }
    }
})();
<commit_msg>Revert "improved protocol pattern matching & version bump"<commit_after>// ==UserScript==
// @name         Giveaway Killer (a.k.a. Giveaway Enhancer)
// @namespace    https://github.com/gekkedev/giveawayHelperEnhancer
// @version      0.5.3
// @description  Enhances Steam key giveaways sites by lots of useful features
// @author       gekkedev
// @match        *://*.marvelousga.com/*
// @match        *://*.dupedornot.com/*
// @match        *://*.indiegala.com/*
// @match        *://*.dogebundle.com/*
// @match        *://*.giveaway.su/*
// @match        *://*.steamgifts.com/*
// @match        *://*.simplo.gg/*
// @match        *://*.giveawayhopper.com/*
// @match        *://*.cubicbundle.com/*
// @match        *://*.treasuregiveaways.com/*
// @match        *://*.goldengiveaways.org/*
// @match        *://*.bananagiveaway.com/*
// @match        *://*.gamehag.com/*
// @match        *://*.steamcommunity.com/openid/login*
// @match        *://*.steamcommunity.com/oauth/login*
// @grant        none
// @updateURL https://raw.githubusercontent.com/gekkedev/giveawayHelperEnhancer/master/giveawayHelperEnhancer.user.js
// @downloadURL https://raw.githubusercontent.com/gekkedev/giveawayHelperEnhancer/master/giveawayHelperEnhancer.user.js
// @require https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/blueimp-md5/2.10.0/js/md5.min.js
// @run-at document-end
// ==/UserScript==

(function() {
    J = jQuery.noConflict(true); //to prevent i.e. broken buttons when a site uses jquery too
    /**
    * Determine what to do for this page based on what's defined in the "config" variable
    *
    * 		hostname: String
    *			The hostname of the site we're setting the config for. Must be the same as what's defined
    *			as @match in the metadata block above.
    *
    *		googleads: Boolean
    * 			This tells us if the site we visit has ads (this refers to banners of any size). We'll then trigger the removal function for that.
    *
    *		autologin: String
    * 			This is a selector for a link which has to be visited in order to get forwarded to the login page.
    *
    *		clickables: Array of String
    * 			These are CSS Selectors which will be buttons to be clicked (in ascending order). Some may not be clickable but each click that doesn't need to be done is already a success.
    *
    */
    var removePopups = (function(){
        //console.log("removing popups!");
        removeElement("a[id^='popup']");
    });
    var fakeClickLinks = function() {
        J("form[action*='verify_click.php']").trigger("submit");
    };
    var solveGamehagUsernameCheck = function() {
        //"Kruemel" //some random user, just pick another one from the ranklist in case they block this user ;-)
    };
    scanForElement = function(ident) {
        //console.log("found " + ident + " " + jQuery(ident).length + " time(s)");console.log(jQuery(ident));
        if (J(ident).length >= 1) {
            return true;
        } else {
            //console.log('cannot find ' + ident);
            return false;
        }
    };
    hideElement = function(ident) {
        if (scanForElement(ident)) {
            J(ident).hide();
        }
    };
    removeElement = function(ident) {
        if (scanForElement(ident)) {
            J(ident).remove();
        }
    };
    var clickElement = function(ident) {
        if (scanForElement(ident)) {
            J(ident).click();
        }
    };
    var visitLink = (function(ident){
        window.location.replace(J(ident).attr("href"));
    });
    var removeGoogleAds = (function(){
        //console.log("removing google ads!");
        removeElement("ins[class*='adsbygoogle']");
        setTimeout(removeGoogleAds, 1000);
    });
  	var getEncapsedString = function(input, delimiter) {//just gets the first occurrence
        var startpos = input.indexOf(delimiter) + delimiter.length;
        var endpos = input.indexOf(delimiter, startpos);
        return input.substring(startpos, endpos)
    }
    var getRandomInt = function(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    };

    /*var autoJoinGroups = function() {
        //giveaway.su
        var groups = $("button[data-type='steam.group']");
        for (var i = 0; i < groups.length; i++) {
            var group = groups[i];
            $.get("https://steamcommunity.com/profiles/"+$("aside[data-steam-id]").data("steam-id")+"/groups/").always(function(jqXHR) {
                var regexp = new RegExp("href=\"javascript:leaveGroupPrompt\\('"+$(group).data("check")+"',", "g");
                if (jqXHR && regexp.test(jqXHR))
                    toggleActionButton(button);
                else
                    $(button).prop("disabled", false).find(".glyphicon.spin").removeClass("spin");
            });
            group.remove();
        }
    };*/
    var taskSkipper_1 = function() {//giveaway.su/varlick
        if ($("article .extension").length) {
            $("article .extension").addClass("installed");
            var timestamp = $("article .extension").data("timestamp");
            var csrf = $("article .extension").data("csrf");
            var extension = md5(timestamp+""+csrf);
            var secret = $("article .extension").data("secret");
            $.get(window.location.pathname+"?extension="+extension+"&timestamp="+timestamp+"&csrf="+csrf+(secret ? "&secret="+secret : ""), function(response) {
                $("article").replaceWith(response);
                //this is the extension version and should not depend on manual updates
                $("article").attr("extension-version", "chrome-3.4.9");//+chrome.runtime.getManifest().version);

                var button = $("#getKey a");
                var actions = 0;
                $("#actions [data-action-id]").each(function(i,el){
                    actions += parseInt($(el).data("action-id"));
                    if ($(el).find("button[data-type='steam.curator']").length) {
                        $(el).fadeOut();
                    } else if ($(el).find("button[data-type='youtube.subscribe']").length) {
                        $(el).fadeOut();
                    } else if ($(el).find("button[data-type='twitter.follow']").length) {
                        $(el).fadeOut();
                    } else if ($(el).find("button[data-type='steam.game.wishlist']").length) {
                        $(el).fadeOut();
                    } else if ($(el).find("td:contains('Invite')").length) {
                        $(el).fadeOut();
                    }
                });
                $(button).attr("href", $(button).attr("href")+"&actions="+md5(actions)).removeClass("disabled");
                $("#getKey").prepend("Giveaway Killer by gekkedev has skipped some tasks for you, because this site is trying to manipulate the Steam store and to get access over your private data. Key claiming does usually work when you have joined all the required groups. Please use the Giveaway Helper by Citrinate in order to join groups easier. Linking accounts happens at your own risk and is a possible reason of unwanted actions commited via your account (account theft, unwanted purchases, etc.) and is qualifying you for punishments regarding Steam T.O.S. violations.
");
            });
        }
    };
    var taskSkipper_2 = function() {//goldengiveaways
        var tasks = J("a[onclick*='task()']");
        if (tasks.length) {
            tasks.each(function(counter, element){
                J.get(element.href);
            });
            location.reload();
        } else {
            J(".giveaway-new").last().prepend("
Giveaway Killer by gekkedev has skipped some tasks for you, because this site is trying to manipulate the Steam store by asking you to follow specific curators. Doing such would qualify you for punishments regarding Steam T.O.S. violations.

");
        }
    };
    var taskSkipper_3 = function() {//bananagiveaway/bananatic
        //get only facebook and youtube; steam group, twitter, and point collection tasks might be different
        var tasks = J("li:contains('FB'):contains('Like'), li:contains('Share'):not(li:contains('Twitter'))");
        tasks = tasks.add(J("li:contains('YT'):contains('Check')"));
        //tasks = tasks.add(J("li:contains('Share'):contains('Twitter')"));

      	//dig deeper, but keep the task steps together
        tasks = tasks.find("div[class='buttons']");
        var successes = 0;
        J.ajaxSetup({async:false}); //not so smooth looking but easy
        if (tasks.length) {console.log(tasks);
            tasks.each(function(counter, task){
                J.get( //hit the first link...
                    getEncapsedString(J(J(task).find("button:not([disabled])")).attr("onclick"), "'"),
                    function() {//...and only continue with the second link on success...
                        //which will, on success, make sure we really solved the task
                        J.get(
                            getEncapsedString(J(J(task).find("button[disabled]")).attr("onclick"), "'"),
                            function(){
                                successes++;
                            }
                        );
                    }
                );
            });
            J.ajaxSetup({async:true});
            if (tasks.length == successes) {
                location.reload();
            } else {
                console.log("Could not solve all solveable tasks!");
            }
        } else {
            J("div[class='bottom'] span[class='status']").prepend("
Giveaway Killer by gekkedev has skipped some tasks for you, because this site is exploiting it's users by excessive ad banner usage plus making users fullfill specific tasks which generate a dozen times more revenue than what is offered to the user.

");
        }
    };

    //configuration

    var config = [
        {
            hostname: "marvelousga.com",
            ads: true,
            autologin: "a[href='?login']",
            //clickables: ["button[type=submit][name!=sg][id!=submit]"],
            //autosolve: "'div.collapse* button'"
            trigger: [removePopups, solveGamehagUsernameCheck] //fakeClickLinks
        },
        {
            hostname: "dupedornot.com",
            ads: true,
            autologin: "a[href='?login']",
            trigger: [removePopups]
        },
        {
            hostname: "dogebundle.com",
            ads: false,
            autologin: "a[href*='?page=login']"
        },
        {
            hostname: "indiegala.com",
            ads: true,
            clickables: ["button#close-socials"],
            custom: function() {J('#myModal-givform').modal('show');}
        },
        {
            hostname: "giveaway.su",
            ads: true,
            autologin: "a.steam-login",
            trigger: [taskSkipper_1],
            custom: function() {hideElement("ul.menu");}
        },
        {
            hostname: "simplo.gg",
            ads: true,
            autologin: "div.steam-button a",
            clickables: ["w-div[role=dialog] span"]
        },
        {
            hostname: "steamgifts.com",
            ads: true,
            autologin: "a.nav__sits"
        },
        {
            hostname: "chubbykeys.com",
            ads: false
        },
        {
            hostname: "giveawayhopper.com",
            ads: false,
            autologin: "a[href='?login']"
        },
        {
            hostname: "cubicbundle.com",
            ads: false,
            autologin: "a.no-login"
        },
        {
            hostname: "treasuregiveaways.com",
            clickables: ["input[onclick*=incr]"],
            custom: function() {J('form[action="?login"]').submit();}
        },
        {
            hostname: "goldengiveaways.org",
            ads: false,
            autologin: "a[href='/?login']",
            trigger: [taskSkipper_2]
        },
        {
            hostname: "bananagiveaway.com",
            ads: true,
            autologin: "a[href*='/steam/']",
            trigger: [taskSkipper_3]
        },
        {
            hostname: "gamehag.com",
            ads: true
        },
        {
            hostname: "steamcommunity.com",
            ads: false,
            clickables: ["input[type='submit']"]
        }
    ];

    //execution
    for(var i = 0; i < config.length; i++) {
        var site = config[i];

        if(document.location.hostname.split(".").splice(-2).join(".") == site.hostname) {
            if (site.ads) {
                removeGoogleAds();
                //removePopups();
                //add other ad services here if known
            }
            if (site.autologin !== undefined) {
                if (scanForElement(site.autologin)) {
                    console.log('performing autologin');
                    visitLink(site.autologin);
                }
            }
            if (site.clickables !== undefined) {
                if (site.clickables.length > 0) {
                    for(var j = 0; j < site.clickables.length; j++) {
                        if (J.isArray(site.clickables[j])) {
                            j = getRandomInt(0, site.clickables[j].length);
                            console.log("picking the entry " + j);
                        }
                        clickElement(site.clickables[j]);
                    }
                }
            }
            if (site.trigger !== undefined) {
                for(var j = 0; j < site.trigger.length; j++) {
                    site.trigger[j]();
                }
            }
            if (site.custom !== undefined) {
                site.custom();
            }
        }
    }
})();
<|endoftext|>


<commit_before>(function (global, undefined) {
  "use strict";

  var dict = ${json:strings};

  if (global._L === undefined) {
    global._L = function () {
      var str, key, args = Array.prototype.slice.call(arguments);

      key = args.shift();

      if (dict[key] === undefined) {
        return 'XXXXXXXX ' + key + ' XXXXXXXX';
      }

      str = dict[key].md;
      str = String.prototype.format.apply(str, args);
      return str;
    }
  }
} (window));
<commit_msg>Updated default result for _L in javascript to be the raw version and added a function for retrieving the markdown version<commit_after>(function (global, undefined) {
  "use strict";

  var dict = ${json:strings};

  function noValue(key) {
    return 'XXXXXXXX ' + key + ' XXXXXXXX';
  }

  function formatValue(val, args) {
    return String.prototype.format.apply(val, args);
  }

  if (global._L === undefined) {
    global._L = function () {
      var key, args = Array.prototype.slice.call(arguments);

      key = args.shift();

      if (dict[key] === undefined) {
        return noValue(key);
      } else {
        return formatValue(dict[key].raw, args);
      }
    };
  }

  if (global._MD === undefined) {
    global._MD = function () {
      var key, args = Array.prototype.slice.call(arguments);

      key = args.shift();

      if (dict[key] === undefined) {
        return noValue(key);
      } else {
        return formatValue(dict[key].md, args);
      }
    };
  }

} (window));
<|endoftext|>