<commit_before>/*global describe:true, it:true, before:true, after:true */

var
	chai = require('chai'),
	assert = chai.assert,
	expect = chai.expect,
	should = chai.should()
	;

var
	child = require('child_process'),
	fs = require('fs'),
	path = require('path'),
	powerline = require('../powerline')
	;

function execute(args, callback)
{
	child.exec('./powerline.js' + args, function(err, stdout, stderr)
	{
		if (err)
			throw(err);

		callback(stderr, stdout);
	});
}

describe('powerline.js', function()
{
	describe('#parseOptions', function()
	{
		it('parses an empty options array', function()
		{
			var opts = powerline.parseOptions();
			opts.should.be.an('object');
			Object.keys(opts).length.should.equal(0);
		});

		it('parses a typical array', function()
		{
			var opts = powerline.parseOptions(['--shell', 'bash', '--depth', '3', '2']);
			Object.keys(opts).length.should.equal(3);
			opts.shell.should.equal('bash');
			opts.depth.should.equal(3);
			opts.error.should.equal(true);
		});

	});

	describe('#constructor', function()
	{
		it('is constructed with reasonable defaults', function()
		{
			var p = new powerline.Powerline();
			p.should.have.property('options');
			p.options.should.be.an('object');
			p.options.shell.should.equal('zsh');
			p.options.error.should.equal(false);
			p.options.mode.should.equal('patched');
			p.options.depth.should.equal(5);
			p.options.showRepo.should.equal(true);
			p.options.showPath.should.equal(true);
			p.options.cwdOnly.should.equal(false);
		});

		it('generates a prompt with the given options', function(done)
		{
			var p = new powerline.Powerline();
			p.cwd = '~/projects/powerline-js';
			p.buildPrompt(function()
			{
				p.segments.length.should.equal(5);
				p.segments[0].content.should.equal(' ~ ');
				p.segments[3].content.indexOf(' master').should.equal(0);
				done();
			});
		});

		it('throws when passed an unknown shell', function()
		{
			var badOptions = function()
			{
				var p = new Powerline({ shell: 'tcsh'});
			};

			badOptions.should.throw();
		});

		it('obeys the mode option', function()
		{
			var p = new powerline.Powerline({mode: 'compatible'});
			p.options.mode.should.equal('compatible');
			p.separator.should.equal('\u25b6');
			p.separator_thin.should.equal('\u276f');

		});

		it('obeys the repo-only option TBD', function()
		{

		});

		it('obeys the no-repo option TBD', function()
		{

		});
	});

	describe('#draw', function()
	{
		it('returns a string with color escape sequences TBD', function(done)
		{
			var p = new powerline.Powerline();
			p.buildPrompt(function()
			{
				var result = p.draw();
				result.should.be.a('string');
				done();
			});
		});

	});

	describe('#working directory', function()
	{
		var deepPath = '/this/is/a/deep/path/certainly/ohyes';

		it('obeys the depth option', function()
		{
			var p = new powerline.Powerline({depth: 6});
			p.cwd = path.normalize(process.cwd() + deepPath);
			p.addCWDSegment();
			p.segments.length.should.equal(7);
			p.segments[0].content.should.equal(' ~ ');
			p.segments[2].content.should.equal(' … ');
			p.segments[6].content.should.equal(' ohyes ');
		});

		it('handles very shallow depths gracefully', function()
		{
			var p = new powerline.Powerline({depth: 1});
			p.cwd = path.normalize(process.cwd() + deepPath);
			p.addCWDSegment();
			p.segments.length.should.equal(1);
			p.segments[0].content.should.equal(' ohyes ');
		});

		it('obeys the cwd-only option', function()
		{
			var p = new powerline.Powerline({cwdOnly: true});
			p.addCWDSegment();
			p.segments.length.should.equal(1);
			p.segments[0].content.should.equal(' powerline-js ');
		});

	});

	describe('#git', function()
	{
		it('displays the git repo branch', function(done)
		{
			var p = new powerline.Powerline();
			p.addGitSegment(function()
			{
				p.segments.length.should.equal(1);
				p.segments[0].content.indexOf(' master').should.equal(0);
				done();
			});
		});

		it('displays the presence of untracked files', function(done)
		{
			child.exec('touch foobar.txt', function(err, stdout, stderr)
			{
				var p = new powerline.Powerline();
				p.addGitSegment(function()
				{
					p.segments.length.should.equal(1);
					p.segments[0].content.indexOf('+').should.be.above(7);
					fs.unlinkSync('foobar.txt');
					done();
				});
			});
		});

		it('displays the repo with a red background if files are modified', function(done)
		{
			child.exec('echo "blat blat" > README.md', function(err, stdout, stderr)
			{
				var p = new powerline.Powerline();
				p.addGitSegment(function()
				{
					p.segments.length.should.equal(1);
					p.segments[0].fg.should.equal(15);
					p.segments[0].bg.should.equal(161);
					done();
				});
			});
		});

		it('displays the repo with a green background if files are unmodified', function(done)
		{
			child.exec('git checkout -- README.md', function(err, stdout, stderr)
			{
				var p = new powerline.Powerline();
				p.addGitSegment(function()
				{
					p.segments.length.should.equal(1);
					p.segments[0].fg.should.equal(0);
					p.segments[0].bg.should.equal(148);
					done();
				});
			});
		});

		it('shows upstream changes TBD', function()
		{

		});

		it('shows unpushed changes TBD', function()
		{

		});
	});

	describe('#svn', function()
	{

	});

	describe('#hg', function()
	{
		// feature unimplemented
	});
});
<commit_msg>Implmented two of the stubbed-out tests.<commit_after>/*global describe:true, it:true, before:true, after:true */

var
	chai = require('chai'),
	assert = chai.assert,
	expect = chai.expect,
	should = chai.should()
	;

var
	child = require('child_process'),
	fs = require('fs'),
	path = require('path'),
	powerline = require('../powerline')
	;

function execute(args, callback)
{
	child.exec('./powerline.js' + args, function(err, stdout, stderr)
	{
		if (err)
			throw(err);

		callback(stderr, stdout);
	});
}

describe('powerline.js', function()
{
	describe('#parseOptions', function()
	{
		it('parses an empty options array', function()
		{
			var opts = powerline.parseOptions();
			opts.should.be.an('object');
			Object.keys(opts).length.should.equal(0);
		});

		it('parses a typical array', function()
		{
			var opts = powerline.parseOptions(['--shell', 'bash', '--depth', '3', '2']);
			Object.keys(opts).length.should.equal(3);
			opts.shell.should.equal('bash');
			opts.depth.should.equal(3);
			opts.error.should.equal(true);
		});

	});

	describe('#constructor', function()
	{
		it('is constructed with reasonable defaults', function()
		{
			var p = new powerline.Powerline();
			p.should.have.property('options');
			p.options.should.be.an('object');
			p.options.shell.should.equal('zsh');
			p.options.error.should.equal(false);
			p.options.mode.should.equal('patched');
			p.options.depth.should.equal(5);
			p.options.showRepo.should.equal(true);
			p.options.showPath.should.equal(true);
			p.options.cwdOnly.should.equal(false);
		});

		it('generates a prompt with the given options', function(done)
		{
			var p = new powerline.Powerline();
			p.cwd = '~/projects/powerline-js';
			p.buildPrompt(function()
			{
				p.segments.length.should.equal(5);
				p.segments[0].content.should.equal(' ~ ');
				p.segments[3].content.indexOf(' master').should.equal(0);
				done();
			});
		});

		it('throws when passed an unknown shell', function()
		{
			var badOptions = function()
			{
				var p = new Powerline({ shell: 'tcsh'});
			};

			badOptions.should.throw();
		});

		it('obeys the mode option', function()
		{
			var p = new powerline.Powerline({mode: 'compatible'});
			p.options.mode.should.equal('compatible');
			p.separator.should.equal('\u25b6');
			p.separator_thin.should.equal('\u276f');

		});

		it('obeys the repo-only option', function(done)
		{
			var opts = powerline.parseOptions(['--repo-only']);
			var p = new powerline.Powerline(opts);
			p.cwd = '~/projects/powerline-js';
			p.buildPrompt(function()
			{
				p.segments.length.should.equal(2);
				p.segments[0].content.indexOf(' master').should.equal(0);
				p.segments[1].content.indexOf('$').should.equal(3);
				done();
			});
		});

		it('obeys the no-repo option', function(done)
		{
			var opts = powerline.parseOptions(['--no-repo']);
			var p = new powerline.Powerline(opts);
			p.cwd = '~/projects/powerline-js';
			p.buildPrompt(function()
			{
				p.segments.length.should.equal(4);
				p.segments[3].content.indexOf('$').should.equal(3);
				done();
			});
		});
	});

	describe('#draw', function()
	{
		it('returns a string with color escape sequences TBD', function(done)
		{
			var p = new powerline.Powerline();
			p.buildPrompt(function()
			{
				var result = p.draw();
				result.should.be.a('string');
				done();
			});
		});

	});

	describe('#working directory', function()
	{
		var deepPath = '/this/is/a/deep/path/certainly/ohyes';

		it('obeys the depth option', function()
		{
			var p = new powerline.Powerline({depth: 6});
			p.cwd = path.normalize(process.cwd() + deepPath);
			p.addCWDSegment();
			p.segments.length.should.equal(7);
			p.segments[0].content.should.equal(' ~ ');
			p.segments[2].content.should.equal(' … ');
			p.segments[6].content.should.equal(' ohyes ');
		});

		it('handles very shallow depths gracefully', function()
		{
			var p = new powerline.Powerline({depth: 1});
			p.cwd = path.normalize(process.cwd() + deepPath);
			p.addCWDSegment();
			p.segments.length.should.equal(1);
			p.segments[0].content.should.equal(' ohyes ');
		});

		it('obeys the cwd-only option', function()
		{
			var p = new powerline.Powerline({cwdOnly: true});
			p.addCWDSegment();
			p.segments.length.should.equal(1);
			p.segments[0].content.should.equal(' powerline-js ');
		});

	});

	describe('#git', function()
	{
		it('displays the git repo branch', function(done)
		{
			var p = new powerline.Powerline();
			p.addGitSegment(function()
			{
				p.segments.length.should.equal(1);
				p.segments[0].content.indexOf(' master').should.equal(0);
				done();
			});
		});

		it('displays the presence of untracked files', function(done)
		{
			child.exec('touch foobar.txt', function(err, stdout, stderr)
			{
				var p = new powerline.Powerline();
				p.addGitSegment(function()
				{
					p.segments.length.should.equal(1);
					p.segments[0].content.indexOf('+').should.be.above(7);
					fs.unlinkSync('foobar.txt');
					done();
				});
			});
		});

		it('displays the repo with a red background if files are modified', function(done)
		{
			child.exec('echo "blat blat" > README.md', function(err, stdout, stderr)
			{
				var p = new powerline.Powerline();
				p.addGitSegment(function()
				{
					p.segments.length.should.equal(1);
					p.segments[0].fg.should.equal(15);
					p.segments[0].bg.should.equal(161);
					done();
				});
			});
		});

		it('displays the repo with a green background if files are unmodified', function(done)
		{
			child.exec('git checkout -- README.md', function(err, stdout, stderr)
			{
				var p = new powerline.Powerline();
				p.addGitSegment(function()
				{
					p.segments.length.should.equal(1);
					p.segments[0].fg.should.equal(0);
					p.segments[0].bg.should.equal(148);
					done();
				});
			});
		});

		it('shows upstream changes TBD', function()
		{

		});

		it('shows unpushed changes TBD', function()
		{

		});
	});

	describe('#svn', function()
	{

	});

	describe('#hg', function()
	{
		// feature unimplemented
	});
});
<|endoftext|>


<commit_before>#!/usr/bin/env node

var e = require( '../expector' )
  , test = require( 'tape' );

test( 'smoke test', function(t) {
  var expector = new e.Expector(t);
  expector.expect( 'hello' );
  expector.emit( 'hello' );
  expector.check();
});

test( 'assert test', function(t) {
  var expector = new e.Expector();
  expector.expect( 'hello' );
  expector.emit( 'hello' );
  expector.check();
  t.pass();
  t.end();
});

test( 'test emit return', function(t) {
  var expector = new e.Expector(t);
  expector.expect('hello').emit('hello').check();
});

test( 'test example', function(t) {
  var a = new e.SeqExpector();
  a.expect( 'first' );
  a.expect( 'second' );
  try {
    a.emit( 'second' ); // => fails, expecting first
    t.fail();
    t.end();
  }
  catch(err) {
    t.pass();
    t.end();
  };
});<commit_msg>failing test<commit_after>#!/usr/bin/env node

var e = require( '../expector' )
  , test = require( 'tape' );

test( 'smoke test', function(t) {
  var expector = new e.Expector(t);
  expector.expect( 'hello' );
  expector.emit( 'hello' );
  expector.check();
});

test( 'assert test', function(t) {
  var expector = new e.Expector();
  expector.expect( 'hello' );
  expector.emit( 'hello' );
  expector.check();
  t.pass();
  t.end();
});

test( 'test emit return', function(t) {
  var expector = new e.Expector(t);
  expector.expect('hello').emit('hello').check();
});

test( 'test example', function(t) {
  var a = new e.SeqExpector();
  a.expect( 'first' );
  a.expect( 'second' );
  try {
    a.emit( 'second' ); // => fails, expecting first
    t.fail();
    t.end();
  }
  catch(err) {
    t.pass();
    t.end();
  };
});

test.only( 'test object stability', function(t) {
  var expector = new e.Expector(t);
  expector.expect({ a: 'h', b: 2}).emit({ b: 2, a: 'h'}).check();
});<|endoftext|>


<commit_before>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require("../net2/logger.js")(__filename);

const rclient = require('../util/redis_manager.js').getRedisClient()

const DNSMASQ = require('../extension/dnsmasq/dnsmasq.js');
const dnsmasq = new DNSMASQ();

const Block = require('./Block.js');

const DNSTool = require('../net2/DNSTool.js')
const dnsTool = new DNSTool()

const dns = require('dns');
const util = require('util');
const resolver = new dns.Resolver();
let resolve4Async;
let resolve6Async;
const fc = require('../net2/config.js');
const dc = require('../extension/dnscrypt/dnscrypt');

const SysManager = require("../net2/SysManager.js")
const sysManager = new SysManager()

const sem = require('../sensor/SensorEventManager.js').getInstance()
const DomainUpdater = require('./DomainUpdater.js');
const domainUpdater = new DomainUpdater();
const DomainIPTool = require('./DomainIPTool.js');
const domainIPTool = new DomainIPTool();
class DomainBlock {

  constructor() {
    sem.once('IPTABLES_READY', async () => {
      sem.on('UPDATE_CATEGORY_DEFAULT_DOMAIN', async (event) => {
        if (event.category) {
          const PM2 = require('../alarm/PolicyManager2.js');
          const pm2 = new PM2();
          const policies = await pm2.loadActivePoliciesAsync();
          for (const policy of policies) {
            if (policy.type == "category" && policy.target == event.category && policy.dnsmasq_entry) {
              await pm2.tryPolicyEnforcement(policy, 'reenforce', policy)
            }
          }
        }
      })
    })
  }

  // a mapping from domain to ip is tracked in redis, so that we can apply block at ip level, which is more secure
  async blockDomain(domain, options) {
    options = options || {}
    log.info(`Implementing Block on ${domain}`);

    if (options.dnsmasq_entry) {
      await dnsmasq.addPolicyFilterEntry([domain], options).catch((err) => undefined);
      sem.emitEvent({
        type: 'ReloadDNSRule',
        message: 'DNSMASQ filter rule is updated',
        toProcess: 'FireMain',
        suppressEventLogging: true
      })
      return;
    }

    await this.syncDomainIPMapping(domain, options)
    domainUpdater.registerUpdate(domain, options);
    if (!options.ignoreApplyBlock) {
      await this.applyBlock(domain, options);
    }

    // setTimeout(() => {
    //   this.incrementalUpdateIPMapping(domain, options)
    // }, 60 * 1000) // reinforce in 60 seconds
  }

  async unblockDomain(domain, options) {
    if (options.dnsmasq_entry) {
      await dnsmasq.removePolicyFilterEntry([domain], options).catch((err) => undefined);
      sem.emitEvent({
        type: 'ReloadDNSRule',
        message: 'DNSMASQ filter rule is updated',
        toProcess: 'FireMain',
        suppressEventLogging: true,
      })
      return
    }
    if (!options.ignoreUnapplyBlock) {
      await this.unapplyBlock(domain, options);
    }

    if (!this.externalMapping) {
      await domainIPTool.removeDomainIPMapping(domain, options);
    }

    domainUpdater.unregisterUpdate(domain, options);
  }

  async applyBlock(domain, options) {
    const blockSet = options.blockSet || "blocked_domain_set"
    const addresses = await domainIPTool.getMappedIPAddresses(domain, options);
    if (addresses) {
      for (const addr of addresses) {
        try {
          await Block.block(addr, blockSet)
        } catch (err) { }
      }
    }
  }

  async unapplyBlock(domain, options) {
    const blockSet = options.blockSet || "blocked_domain_set"

    const addresses = await domainIPTool.getMappedIPAddresses(domain, options);
    if (addresses) {
      for (const addr of addresses) {
        try {
          Block.unblock(addr, blockSet)
        } catch (err) { }
      }
    }
  }

  resolve4WithTimeout(domain, timeout) {
    let callbackCalled = false
    return new Promise((resolve, reject) => {
      resolve4Async(domain).then((addresses) => {
        if (!callbackCalled) {
          callbackCalled = true
          resolve(addresses)
        }
      }).catch((err) => {
        if (!callbackCalled) {
          callbackCalled = true
          resolve([]) // return empty array in case any error
        }
      })
      setTimeout(() => {
        if (!callbackCalled) {
          callbackCalled = true
          resolve([]) // return empty array in case timeout
        }
      }, timeout)
    })
  }

  resolve6WithTimeout(domain, timeout) {
    let callbackCalled = false

    return new Promise((resolve, reject) => {
      resolve6Async(domain).then((addresses) => {
        if (!callbackCalled) {
          callbackCalled = true
          resolve(addresses)
        }
      }).catch((err) => {
        if (!callbackCalled) {
          callbackCalled = true
          resolve([]) // return empty array in case any error
        }
      })
      setTimeout(() => {
        if (!callbackCalled) {
          log.warn("Timeout when query domain", domain)
          callbackCalled = true
          resolve([]) // return empty array in case timeout
        }
      }, timeout)
    })
  }

  async resolveDomain(domain) {
    if (fc.isFeatureOn('doh')) {
      const server = `127.0.0.1:${dc.getLocalPort()}`;
 //     resolver.setServers([server]);
      resolve4Async = util.promisify(resolver.resolve4.bind(resolver));
      resolve6Async = util.promisify(resolver.resolve6.bind(resolver));
    } else {
      resolve4Async = util.promisify(dns.resolve4);
      resolve6Async = util.promisify(dns.resolve6);
    }
    const v4Addresses = await this.resolve4WithTimeout(domain, 3 * 1000).catch((err) => []); // 3 seconds for timeout
    await dnsTool.addReverseDns(domain, v4Addresses);

    const gateway6 = sysManager.myGateway6()
    if (gateway6) { // only query if ipv6 is supported
      const v6Addresses = await this.resolve6WithTimeout(domain, 3 * 1000).catch((err) => []);
      await dnsTool.addReverseDns(domain, v6Addresses);
      return v4Addresses.concat(v6Addresses)
    } else {
      return v4Addresses
    }
  }

  async syncDomainIPMapping(domain, options) {
    options = options || {}

    const key = domainIPTool.getDomainIPMappingKey(domain, options)

    await this.resolveDomain(domain); // this will resolve domain via dns and add entries into reverse dns directly

    let list = []

    // load other addresses from rdns, critical to apply instant blocking
    const addresses = await dnsTool.getIPsByDomain(domain).catch((err) => []);
    list.push.apply(list, addresses)  // concat arrays

    if (!options.exactMatch) {
      const patternAddresses = await dnsTool.getIPsByDomainPattern(domain).catch((err) => []);
      list.push.apply(list, patternAddresses)
    }

    return rclient.saddAsync(key, list)
  }

  // incremental update mapping to reinforce ip blocking
  // this function should be executed in a serial way with other policy enforcements to avoid race conditions
  async incrementalUpdateIPMapping(domain, options) {
    options = options || {}

    log.info("Incrementally updating blocking list for", domain)

    const key = domainIPTool.getDomainIPMappingKey(domain, options)

    const existing = await rclient.existsAsync(key);

    if (!existing) {
      return
    }

    await this.resolveDomain(domain); // this will resolve domain via dns and add entries into reverse dns directly

    let set = {}

    // load other addresses from rdns, critical to apply instant blocking
    const addresses = await dnsTool.getIPsByDomain(domain).catch((err) => []);
    addresses.forEach((addr) => {
      set[addr] = 1
    })

    if (!options.exactMatch) {
      const patternAddresses = await dnsTool.getIPsByDomainPattern(domain).catch((err) => []);
      patternAddresses.forEach((addr) => {
        set[addr] = 1
      })
    }

    const existingAddresses = await domainIPTool.getMappedIPAddresses(domain, options);

    let existingSet = {}
    existingAddresses.forEach((addr) => {
      existingSet[addr] = 1
    })

    // only add new changed ip addresses, there is no need to remove any old ip addrs
    for (let addr in set) {
      if (!existingSet[addr]) {
        await rclient.saddAsync(key, addr);
        let blockSet = "blocked_domain_set";
        if (options.blockSet)
          blockSet = options.blockSet;
        await Block.block(addr, blockSet).catch((err) => undefined);
      }
    }
  }
  async blockCategory(category, options) {
    const domains = await this.getCategoryDomains(category);
    await dnsmasq.addPolicyCategoryFilterEntry(domains, options).catch((err) => undefined);
    sem.emitEvent({
      type: 'ReloadDNSRule',
      message: 'DNSMASQ filter rule is updated',
      toProcess: 'FireMain',
      suppressEventLogging: true
    })
  }

  async unblockCategory(category, options) {
    const domains = await this.getCategoryDomains(category);
    await dnsmasq.removePolicyCategoryFilterEntry(domains, options).catch((err) => undefined);
    sem.emitEvent({
      type: 'ReloadDNSRule',
      message: 'DNSMASQ filter rule is updated',
      toProcess: 'FireMain',
      suppressEventLogging: true,
    })
  }
  async updateCategoryBlock(category) {
    const domains = await this.getCategoryDomains(category);
    await dnsmasq.updatePolicyCategoryFilterEntry(domains, { category: category });
  }
  async getCategoryDomains(category) {
    const CategoryUpdater = require('./CategoryUpdater.js');
    const categoryUpdater = new CategoryUpdater();
    const domains = await categoryUpdater.getDomainsWithExpireTime(category);
    const excludedDomains = await categoryUpdater.getExcludedDomains(category);
    const defaultDomains = await categoryUpdater.getDefaultDomains(category);
    const includedDomains = await categoryUpdater.getIncludedDomains(category);
    const finalDomains = domains.filter((de) => {
      return !excludedDomains.includes(de.domain) && !defaultDomains.includes(de.domain)
    }).map((de) => { return de.domain }).concat(defaultDomains, includedDomains)

    function dedupAndPattern(arr) {
      const pattern = arr.filter((domain) => {
        return domain.startsWith("*.")
      }).map((domain) => domain.substring(2))
      return Array.from(new Set(arr.filter((domain) => {
        if (!domain.startsWith("*.") && pattern.includes(domain)) {
          return false;
        } else if (domain.startsWith("*.")) {
          return false;
        } else {
          return true;
        }
      }).concat(pattern)))
    }
    return dedupAndPattern(finalDomains)
  }
  patternDomain(domain) {
    domain = domain || "";
    if (domain.startsWith("*.")) {
      domain = domain.substring(2);
    }
    return domain;
  }
}

module.exports = () => new DomainBlock()
<commit_msg>bug fix<commit_after>/*    Copyright 2016 Firewalla LLC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require("../net2/logger.js")(__filename);

const rclient = require('../util/redis_manager.js').getRedisClient()

const DNSMASQ = require('../extension/dnsmasq/dnsmasq.js');
const dnsmasq = new DNSMASQ();

const Block = require('./Block.js');

const DNSTool = require('../net2/DNSTool.js')
const dnsTool = new DNSTool()

const dns = require('dns');
const util = require('util');
const resolver = new dns.Resolver();
let resolve4Async;
let resolve6Async;
const fc = require('../net2/config.js');
const dc = require('../extension/dnscrypt/dnscrypt');

const SysManager = require("../net2/SysManager.js")
const sysManager = new SysManager()

const sem = require('../sensor/SensorEventManager.js').getInstance()
const DomainUpdater = require('./DomainUpdater.js');
const domainUpdater = new DomainUpdater();
const DomainIPTool = require('./DomainIPTool.js');
const domainIPTool = new DomainIPTool();
class DomainBlock {

  constructor() {
    sem.once('IPTABLES_READY', async () => {
      sem.on('UPDATE_CATEGORY_DEFAULT_DOMAIN', async (event) => {
        if (event.category) {
          const PM2 = require('../alarm/PolicyManager2.js');
          const pm2 = new PM2();
          const policies = await pm2.loadActivePoliciesAsync();
          for (const policy of policies) {
            if (policy.type == "category" && policy.target == event.category && policy.dnsmasq_entry) {
              await pm2.tryPolicyEnforcement(policy, 'reenforce', policy)
            }
          }
        }
      })
    })
  }

  // a mapping from domain to ip is tracked in redis, so that we can apply block at ip level, which is more secure
  async blockDomain(domain, options) {
    options = options || {}
    log.info(`Implementing Block on ${domain}`);

    if (options.dnsmasq_entry) {
      await dnsmasq.addPolicyFilterEntry([domain], options).catch((err) => undefined);
      sem.emitEvent({
        type: 'ReloadDNSRule',
        message: 'DNSMASQ filter rule is updated',
        toProcess: 'FireMain',
        suppressEventLogging: true
      })
      return;
    }

    await this.syncDomainIPMapping(domain, options)
    domainUpdater.registerUpdate(domain, options);
    if (!options.ignoreApplyBlock) {
      await this.applyBlock(domain, options);
    }

    // setTimeout(() => {
    //   this.incrementalUpdateIPMapping(domain, options)
    // }, 60 * 1000) // reinforce in 60 seconds
  }

  async unblockDomain(domain, options) {
    if (options.dnsmasq_entry) {
      await dnsmasq.removePolicyFilterEntry([domain], options).catch((err) => undefined);
      sem.emitEvent({
        type: 'ReloadDNSRule',
        message: 'DNSMASQ filter rule is updated',
        toProcess: 'FireMain',
        suppressEventLogging: true,
      })
      return
    }
    if (!options.ignoreUnapplyBlock) {
      await this.unapplyBlock(domain, options);
    }

    if (!this.externalMapping) {
      await domainIPTool.removeDomainIPMapping(domain, options);
    }

    domainUpdater.unregisterUpdate(domain, options);
  }

  async applyBlock(domain, options) {
    const blockSet = options.blockSet || "blocked_domain_set"
    const addresses = await domainIPTool.getMappedIPAddresses(domain, options);
    if (addresses) {
      for (const addr of addresses) {
        try {
          await Block.block(addr, blockSet)
        } catch (err) { }
      }
    }
  }

  async unapplyBlock(domain, options) {
    const blockSet = options.blockSet || "blocked_domain_set"

    const addresses = await domainIPTool.getMappedIPAddresses(domain, options);
    if (addresses) {
      for (const addr of addresses) {
        try {
          Block.unblock(addr, blockSet)
        } catch (err) { }
      }
    }
  }

  resolve4WithTimeout(domain, timeout) {
    let callbackCalled = false
    return new Promise((resolve, reject) => {
      resolve4Async(domain).then((addresses) => {
        if (!callbackCalled) {
          callbackCalled = true
          resolve(addresses)
        }
      }).catch((err) => {
        if (!callbackCalled) {
          callbackCalled = true
          resolve([]) // return empty array in case any error
        }
      })
      setTimeout(() => {
        if (!callbackCalled) {
          callbackCalled = true
          resolve([]) // return empty array in case timeout
        }
      }, timeout)
    })
  }

  resolve6WithTimeout(domain, timeout) {
    let callbackCalled = false

    return new Promise((resolve, reject) => {
      resolve6Async(domain).then((addresses) => {
        if (!callbackCalled) {
          callbackCalled = true
          resolve(addresses)
        }
      }).catch((err) => {
        if (!callbackCalled) {
          callbackCalled = true
          resolve([]) // return empty array in case any error
        }
      })
      setTimeout(() => {
        if (!callbackCalled) {
          log.warn("Timeout when query domain", domain)
          callbackCalled = true
          resolve([]) // return empty array in case timeout
        }
      }, timeout)
    })
  }

  async resolveDomain(domain) {
    if (false && fc.isFeatureOn('doh')) {
      const server = `127.0.0.1:${dc.getLocalPort()}`;
      resolver.setServers([server]);
      resolve4Async = util.promisify(resolver.resolve4.bind(resolver));
      resolve6Async = util.promisify(resolver.resolve6.bind(resolver));
    } else {
      resolve4Async = util.promisify(dns.resolve4);
      resolve6Async = util.promisify(dns.resolve6);
    }
    const v4Addresses = await this.resolve4WithTimeout(domain, 3 * 1000).catch((err) => []); // 3 seconds for timeout
    await dnsTool.addReverseDns(domain, v4Addresses);

    const gateway6 = sysManager.myGateway6()
    if (gateway6) { // only query if ipv6 is supported
      const v6Addresses = await this.resolve6WithTimeout(domain, 3 * 1000).catch((err) => []);
      await dnsTool.addReverseDns(domain, v6Addresses);
      return v4Addresses.concat(v6Addresses)
    } else {
      return v4Addresses
    }
  }

  async syncDomainIPMapping(domain, options) {
    options = options || {}

    const key = domainIPTool.getDomainIPMappingKey(domain, options)

    await this.resolveDomain(domain); // this will resolve domain via dns and add entries into reverse dns directly

    let list = []

    // load other addresses from rdns, critical to apply instant blocking
    const addresses = await dnsTool.getIPsByDomain(domain).catch((err) => []);
    list.push.apply(list, addresses)  // concat arrays

    if (!options.exactMatch) {
      const patternAddresses = await dnsTool.getIPsByDomainPattern(domain).catch((err) => []);
      list.push.apply(list, patternAddresses)
    }

    return rclient.saddAsync(key, list)
  }

  // incremental update mapping to reinforce ip blocking
  // this function should be executed in a serial way with other policy enforcements to avoid race conditions
  async incrementalUpdateIPMapping(domain, options) {
    options = options || {}

    log.info("Incrementally updating blocking list for", domain)

    const key = domainIPTool.getDomainIPMappingKey(domain, options)

    const existing = await rclient.existsAsync(key);

    if (!existing) {
      return
    }

    await this.resolveDomain(domain); // this will resolve domain via dns and add entries into reverse dns directly

    let set = {}

    // load other addresses from rdns, critical to apply instant blocking
    const addresses = await dnsTool.getIPsByDomain(domain).catch((err) => []);
    addresses.forEach((addr) => {
      set[addr] = 1
    })

    if (!options.exactMatch) {
      const patternAddresses = await dnsTool.getIPsByDomainPattern(domain).catch((err) => []);
      patternAddresses.forEach((addr) => {
        set[addr] = 1
      })
    }

    const existingAddresses = await domainIPTool.getMappedIPAddresses(domain, options);

    let existingSet = {}
    existingAddresses.forEach((addr) => {
      existingSet[addr] = 1
    })

    // only add new changed ip addresses, there is no need to remove any old ip addrs
    for (let addr in set) {
      if (!existingSet[addr]) {
        await rclient.saddAsync(key, addr);
        let blockSet = "blocked_domain_set";
        if (options.blockSet)
          blockSet = options.blockSet;
        await Block.block(addr, blockSet).catch((err) => undefined);
      }
    }
  }
  async blockCategory(category, options) {
    const domains = await this.getCategoryDomains(category);
    await dnsmasq.addPolicyCategoryFilterEntry(domains, options).catch((err) => undefined);
    sem.emitEvent({
      type: 'ReloadDNSRule',
      message: 'DNSMASQ filter rule is updated',
      toProcess: 'FireMain',
      suppressEventLogging: true
    })
  }

  async unblockCategory(category, options) {
    const domains = await this.getCategoryDomains(category);
    await dnsmasq.removePolicyCategoryFilterEntry(domains, options).catch((err) => undefined);
    sem.emitEvent({
      type: 'ReloadDNSRule',
      message: 'DNSMASQ filter rule is updated',
      toProcess: 'FireMain',
      suppressEventLogging: true,
    })
  }
  async updateCategoryBlock(category) {
    const domains = await this.getCategoryDomains(category);
    await dnsmasq.updatePolicyCategoryFilterEntry(domains, { category: category });
  }
  async getCategoryDomains(category) {
    const CategoryUpdater = require('./CategoryUpdater.js');
    const categoryUpdater = new CategoryUpdater();
    const domains = await categoryUpdater.getDomainsWithExpireTime(category);
    const excludedDomains = await categoryUpdater.getExcludedDomains(category);
    const defaultDomains = await categoryUpdater.getDefaultDomains(category);
    const includedDomains = await categoryUpdater.getIncludedDomains(category);
    const finalDomains = domains.filter((de) => {
      return !excludedDomains.includes(de.domain) && !defaultDomains.includes(de.domain)
    }).map((de) => { return de.domain }).concat(defaultDomains, includedDomains)

    function dedupAndPattern(arr) {
      const pattern = arr.filter((domain) => {
        return domain.startsWith("*.")
      }).map((domain) => domain.substring(2))
      return Array.from(new Set(arr.filter((domain) => {
        if (!domain.startsWith("*.") && pattern.includes(domain)) {
          return false;
        } else if (domain.startsWith("*.")) {
          return false;
        } else {
          return true;
        }
      }).concat(pattern)))
    }
    return dedupAndPattern(finalDomains)
  }
  patternDomain(domain) {
    domain = domain || "";
    if (domain.startsWith("*.")) {
      domain = domain.substring(2);
    }
    return domain;
  }
}

module.exports = () => new DomainBlock()
<|endoftext|>


<commit_before>var webviewPluginApp = angular.module('webviewPlugin', []);

webviewPluginApp.controller("webviewPluginCtrl", ["$scope", "$log", "$timeout", function ($scope, $log, $timeout) {
  var dataChanged = false;
  $scope.datastoreInitialized = false;
  $scope.urlValid = false;
  $scope.urlInValid = false;
  $scope.viewType = {
    NATIVE_IN_APP: 'Native In App',
    IN_APP_POPUP: 'In app popup',
    EXTERNAL_BROWSER: 'External browser'
  };
  /*
   * Go pull any previously saved data
   * */
  buildfire.datastore.get(function (err, result) {
    if (!err) {
      $scope.datastoreInitialized = true;
    } else {
      console.error("Error: ", err);
      return;
    }

    if (result && result.data && !angular.equals({}, result.data)) {
      $scope.data = result.data;
      $scope.id = result.id;
      if (typeof result.data.content.openInApp != 'undefined') {
        if (result.data.content.openInApp)
          $scope.data.content.view = $scope.viewType.NATIVE_IN_APP;
        else
          $scope.data.content.view = $scope.viewType.IN_APP_POPUP;
      }
    } else {
      $scope.data = {
        content: {
          url: "",
          view: $scope.viewType.NATIVE_IN_APP
        }
      };
    }

    /*
     * watch for changes in data
     * */
    $scope.$watch('data', function (newObj, oldObj) {
      if (angular.equals(newObj, oldObj) || newObj == undefined) {
        dataChanged = false;
      } else {
        dataChanged = true;
      }
    }, true);

    if (!$scope.$$phase && !$scope.$root.$$phase) {
      $scope.$apply();
    }
  });

  $scope.saveData = function () {
    if (!$scope.datastoreInitialized) {
      console.error("Error with datastore didn't get called");
      return;
    }
    if (!dataChanged) {
      console.warn("data didn't changed");
      return;
    }
    var data = $scope.data;
    // if the form has some invalid data do not save, in our case the user eneter invalid URL
    if ($scope.frmMain.$invalid) {
      $log.warn('invalid data, details will not be saved');
      $scope.urlValid = false;
      $scope.urlInValid = true;
      $timeout(function () {
        $scope.urlInValid = false;
      }, 3000);
    } else {
      $scope.urlValid = true;
      $scope.urlInValid = false;
      $timeout(function () {
        $scope.urlValid = false;
      }, 3000);
      dataChanged = false;
      if (!/^https?\:\/\//.test(data.content.url)) {
        data.content.url = "http://" + data.content.url;
      }

      buildfire.datastore.save(data, function (err, result) {
        if (err || !result) {
          $log.error('Error saving the widget details: ', err);
        }
        else {
          $log.info('Widget details saved');
        }
      });
    }
  };

  $scope.validateUrl = function () {
    $scope.saveData();
  };

  $scope.changeViewType = function () {
    dataChanged = true;
    if (!$scope.frmMain.$invalid) {
      var data = $scope.data;
      buildfire.datastore.save(data, function (err, result) {
        if (err || !result) {
          $log.error('Error saving the widget details: ', err);
        }
        else {
          $log.info('Widget details saved');
        }
      });
    }
  };

  $scope.openMethodChanged = function () {
    dataChanged = true;
    buildfire.datastore.save($scope.data, function (err, result) {
      if (err || !result) {
        $log.error('Error saving the widget details: ', err);
      }
      else {
        $log.info('Widget details saved');
      }
    });
  }
}]);<commit_msg>Removed data.content.openInApp field in case datastore saved is called.<commit_after>var webviewPluginApp = angular.module('webviewPlugin', []);

webviewPluginApp.controller("webviewPluginCtrl", ["$scope", "$log", "$timeout", function ($scope, $log, $timeout) {
  var dataChanged = false;
  $scope.datastoreInitialized = false;
  $scope.urlValid = false;
  $scope.urlInValid = false;
  $scope.viewType = {
    NATIVE_IN_APP: 'Native In App',
    IN_APP_POPUP: 'In app popup',
    EXTERNAL_BROWSER: 'External browser'
  };
  /*
   * Go pull any previously saved data
   * */
  buildfire.datastore.get(function (err, result) {
    if (!err) {
      $scope.datastoreInitialized = true;
    } else {
      console.error("Error: ", err);
      return;
    }

    if (result && result.data && !angular.equals({}, result.data)) {
      $scope.data = result.data;
      $scope.id = result.id;
      if (typeof result.data.content.openInApp != 'undefined') {
        if (result.data.content.openInApp)
          $scope.data.content.view = $scope.viewType.NATIVE_IN_APP;
        else
          $scope.data.content.view = $scope.viewType.IN_APP_POPUP;
      }
    } else {
      $scope.data = {
        content: {
          url: "",
          view: $scope.viewType.NATIVE_IN_APP
        }
      };
    }

    /*
     * watch for changes in data
     * */
    $scope.$watch('data', function (newObj, oldObj) {
      if (angular.equals(newObj, oldObj) || newObj == undefined) {
        dataChanged = false;
      } else {
        dataChanged = true;
      }
    }, true);

    if (!$scope.$$phase && !$scope.$root.$$phase) {
      $scope.$apply();
    }
  });

  $scope.saveData = function () {
    if (!$scope.datastoreInitialized) {
      console.error("Error with datastore didn't get called");
      return;
    }
    if (!dataChanged) {
      console.warn("data didn't changed");
      return;
    }
    var data = $scope.data;
    // if the form has some invalid data do not save, in our case the user eneter invalid URL
    if ($scope.frmMain.$invalid) {
      $log.warn('invalid data, details will not be saved');
      $scope.urlValid = false;
      $scope.urlInValid = true;
      $timeout(function () {
        $scope.urlInValid = false;
      }, 3000);
    } else {
      $scope.urlValid = true;
      $scope.urlInValid = false;
      $timeout(function () {
        $scope.urlValid = false;
      }, 3000);
      dataChanged = false;
      if (!/^https?\:\/\//.test(data.content.url)) {
        data.content.url = "http://" + data.content.url;
      }
      if (data.content.openInApp != 'undefined')
        data.content.openInApp = null;
      buildfire.datastore.save(data, function (err, result) {
        if (err || !result) {
          $log.error('Error saving the widget details: ', err);
        }
        else {
          $log.info('Widget details saved');
        }
      });
    }
  };

  $scope.validateUrl = function () {
    $scope.saveData();
  };

  $scope.changeViewType = function () {
    dataChanged = true;
    if (!$scope.frmMain.$invalid) {
      var data = $scope.data;
      if (data.content.openInApp != 'undefined')
        data.content.openInApp = null;
      buildfire.datastore.save(data, function (err, result) {
        if (err || !result) {
          $log.error('Error saving the widget details: ', err);
        }
        else {
          $log.info('Widget details saved');
        }
      });
    }
  };

  $scope.openMethodChanged = function () {
    dataChanged = true;
    buildfire.datastore.save($scope.data, function (err, result) {
      if (err || !result) {
        $log.error('Error saving the widget details: ', err);
      }
      else {
        $log.info('Widget details saved');
      }
    });
  }
}]);<|endoftext|>


<commit_before>'use strict';
var util = require('util');
var path = require('path');
var yeoman = require('yeoman-generator');
var yosay = require('yosay');
var chalk = require('chalk');

var PhpstylGenerator = yeoman.generators.Base.extend({
  init: function () {
    this.pkg = require('../package.json');

    this.on('end', function () {
      if (!this.options['skip-install']) {
        this.installDependencies();
      }
    });
  },

  askFor: function () {
    var done = this.async();

    // Have Yeoman greet the user.
    this.log(yosay('Welcome to the marvelous Phpstyl generator!'));

    var prompts = [{
      name: 'title',
      message: 'What is the title of your application?',
      default: 'Hello World'
    }];

    this.prompt(prompts, function (props) {
      this.title = props.title;

      done();
    }.bind(this));
  },

   var extractGeneratorName = function (_, appname) {
    var slugged = _.slugify(title);
    var match = slugged.match(/^$/);

    if (match && match.length === 2) {
      return match[1].toLowerCase();
  }

  return slugged;
  };
    }.bind(this));
  },

  app: function () {
    this.mkdir('src');
    this.mkdir('src/scripts');
    this.mkdir('src/styles');

    this.template('src/index.php', 'src/index.php');
    this.template('src/scripts/main.js', 'src/scripts/main.js');
    this.template('src/styles/main.styl', 'src/styles/main.styl');
    this.copy('_package.json', 'package.json');
    this.copy('_bower.json', 'bower.json');
    this.copy('editorconfig', '.editorconfig');
    this.copy('gitignore', '.gitignore');
    this.copy('bowerrc', '.bowerrc');
    this.copy('jshintrc', '.jshintrc');
    this.template('Gruntfile.js', 'Gruntfile.js');
    this.template('README.md', 'README.md');
  },

  projectfiles: function () {
  }
});

module.exports = PhpstylGenerator;
<commit_msg>Update test.js<commit_after>//Todo
'use strict';
var util = require('util');
var path = require('path');
var yeoman = require('yeoman-generator');
var yosay = require('yosay');
var chalk = require('chalk');

var PhpstylGenerator = yeoman.generators.Base.extend({
  init: function () {
    this.pkg = require('../package.json');

    this.on('end', function () {
      if (!this.options['skip-install']) {
        this.installDependencies();
      }
    });
  },

  askFor: function () {
    var done = this.async();

    // Have Yeoman greet the user.
    this.log(yosay('Welcome to the marvelous Phpstyl generator!'));

    var prompts = [{
      name: 'title',
      message: 'What is the title of your application?',
      default: 'Hello World'
    }];

    this.prompt(prompts, function (props) {
      this.title = props.title;

      done();
    }.bind(this));
  },

   var extractGeneratorName = function (_, appname) {
    var slugged = _.slugify(title);
    var match = slugged.match(/^$/);

    if (match && match.length === 2) {
      return match[1].toLowerCase();
  }

  return slugged;
  };
    }.bind(this));
  },

  app: function () {
    this.mkdir('src');
    this.mkdir('src/scripts');
    this.mkdir('src/styles');

    this.template('src/index.php', 'src/index.php');
    this.template('src/scripts/main.js', 'src/scripts/main.js');
    this.template('src/styles/main.styl', 'src/styles/main.styl');
    this.copy('_package.json', 'package.json');
    this.copy('_bower.json', 'bower.json');
    this.copy('editorconfig', '.editorconfig');
    this.copy('gitignore', '.gitignore');
    this.copy('bowerrc', '.bowerrc');
    this.copy('jshintrc', '.jshintrc');
    this.template('Gruntfile.js', 'Gruntfile.js');
    this.template('README.md', 'README.md');
  },

  projectfiles: function () {
  }
});

module.exports = PhpstylGenerator;
<|endoftext|>


<commit_before>'use strict';

(function (angular) {
    angular.module('customerFeedbackPluginContent', ['ngRoute', 'ui.tinymce', 'ui.bootstrap', 'ngRateIt', 'infinite-scroll'])
        //injected ngRoute for routing
        .config(['$routeProvider', function ($routeProvider) {
            $routeProvider
                .when('/', {
                    templateUrl: 'templates/home.html',
                    controllerAs: 'ContentHome',
                    controller: 'ContentHomeCtrl'
                })
                .when('/chat', {
                    templateUrl: 'templates/chat.html',
                    controllerAs: 'ContentChat',
                    controller: 'ContentChatCtrl'
                })
                .when('/chat/:userToken', {
                    templateUrl: 'templates/chat.html',
                    controllerAs: 'ContentChat',
                    controller: 'ContentChatCtrl'
                })
                .otherwise('/');
        }])
        .filter('getImageUrl', ['Buildfire', function (Buildfire) {
            return function (url, width, height, type) {
                if (type == 'resize')
                    return Buildfire.imageLib.resizeImage(url, {
                        width: width,
                        height: height
                    });
                else
                    return Buildfire.imageLib.cropImage(url, {
                        width: width,
                        height: height
                    });
            }
        }])
        .filter('unique', function () {
            return function (collection, keyname) {
                var output = [],
                    keys = [];

                angular.forEach(collection, function (item) {
                    var key = item[keyname];
                    if (keys.indexOf(key) === -1) {
                        keys.push(key);
                        output.push(item);
                    }
                });

                return output;
            };
        })
        .filter('newLine', ['$sce', function ($sce) {
            return function (html) {
                if (html) {
                    html = html.replace(/\n/g, '
');
                    return $sce.trustAsHtml(html);
                }
                else {
                    return "";
                }
            };
        }]);
})(window.angular);<commit_msg>Added loadImage directive for content side<commit_after>'use strict';

(function (angular) {
    angular.module('customerFeedbackPluginContent', ['ngRoute', 'ui.tinymce', 'ui.bootstrap', 'ngRateIt', 'infinite-scroll'])
        //injected ngRoute for routing
        .config(['$routeProvider', function ($routeProvider) {
            $routeProvider
                .when('/', {
                    templateUrl: 'templates/home.html',
                    controllerAs: 'ContentHome',
                    controller: 'ContentHomeCtrl'
                })
                .when('/chat', {
                    templateUrl: 'templates/chat.html',
                    controllerAs: 'ContentChat',
                    controller: 'ContentChatCtrl'
                })
                .when('/chat/:userToken', {
                    templateUrl: 'templates/chat.html',
                    controllerAs: 'ContentChat',
                    controller: 'ContentChatCtrl'
                })
                .otherwise('/');
        }])
        .filter('getImageUrl', ['Buildfire', function (Buildfire) {
            return function (url, width, height, type) {
                if (type == 'resize')
                    return Buildfire.imageLib.resizeImage(url, {
                        width: width,
                        height: height
                    });
                else
                    return Buildfire.imageLib.cropImage(url, {
                        width: width,
                        height: height
                    });
            }
        }])
        .filter('unique', function () {
            return function (collection, keyname) {
                var output = [],
                    keys = [];

                angular.forEach(collection, function (item) {
                    var key = item[keyname];
                    if (keys.indexOf(key) === -1) {
                        keys.push(key);
                        output.push(item);
                    }
                });

                return output;
            };
        })
        .directive("loadImage", [function () {
            return {
                restrict: 'A',
                link: function (scope, element, attrs) {
                    element.attr("src", "../../../../styles/media/holder-" + attrs.loadImage + ".gif");

                    var elem = $("");
                    elem[0].onload = function () {
                        element.attr("src", attrs.finalSrc);
                        elem.remove();
                    };
                    elem.attr("src", attrs.finalSrc);
                }
            };
        }])
        .filter('newLine', ['$sce', function ($sce) {
            return function (html) {
                if (html) {
                    html = html.replace(/\n/g, '
');
                    return $sce.trustAsHtml(html);
                }
                else {
                    return "";
                }
            };
        }]);
})(window.angular);<|endoftext|>


<commit_before>"use strict";
var util = require("util");
var goose = require("../models/mongoose").instance;
var hostnameSchema = require("../models/schema-hostname.js");
var Member = goose.model("Member", hostnameSchema);

module.exports = {
	create: create,
	read: read,
	update: update,
	delete: deleteFn
};

/**
 * @description Create a new entry in the database
 * @param {object} request
 * @param {object} request.body
 * @param {string} request.body.name
 * @param {string} request.body.message
 * @param {string} request.body.address
 * @param {object} request.params
 * @param {string} request.params.NAME
 * @param {string} request.params.ADDRESS
 * @param {string} request.params.MESSAGE
 * @return {response} response
 */
function create (req, res) {

	var NAME = getName(req);
	var ADDRESS = getAddress(req);
	var MESSAGE = req.body.message || req.params.MESSAGE || "";
	console.log("name? ", NAME);
	console.log("address? ", ADDRESS);
	// console.log("gimme everything you got", req.connection);
	// console.log("port? ", req.connection.port);

	Member.create({
		name: NAME,
		address: ADDRESS,
		message: MESSAGE
	}, respond);

	// Member.findOneAndUpdate(
	// 	{ name: name, address: address, message: message },

	// 	{ new: true, upsert: true },

	// 	respond);

	function respond (err, doc) {
		if (err) {
			return res.status(409).json({ err: err });
		}

		console.log("new entry: ", doc);

		res.status(200).send(doc);
	}

}

/**
 * @description get an entry from the database
 * @param {object} request
 * @param {string} request.param
 * @param {object} response
 * @return {response} response
 */
function read (req, res) {

	var NAME = req.params.NAME;
	var ADDRESS = getAddress(req);

	if (NAME) {
		console.log("find single", NAME);
		Member.findOne({ name: NAME, address: ADDRESS }, respond);
	} else {
		// seems dangerous
		console.log("get all");
		Member.find({}, respond);
	}

	function respond (err, docs) {
		console.log("responding with docs", err, docs);
		if (err) {
			return res.send(err);
		}

		if (!docs || !Object.keys(docs).length) {
			return res.status(404).send("Not found");
		}

		res.status(200).send(docs);
	}

}

/**
 * @description Update an entry's IP address or name
 * @param {object} request
 * @param {string} request.param
 * @param {object} response
 * @return {response} response
 */
function update (req, res) {
	// IP will have ":"
	var IP = req.params.ADDRESS;
	var NAME = req.params.NAME;
	var MESSAGE = req.body.message || req.params.MESSAGE;

	Member.findOneAndUpdate(
		{ name: NAME, address: IP, message: MESSAGE, updated_at: Date.now() },

		{ new: true },

		respond);

	function respond (err, newDoc) {
		if (err) {
			return res.send(err);
		}

		if (!newDoc.length) {
			return res.status(404).send("Not found");
		}

		res.status(200).send(newDoc);
	}

}

/**
 * @description delete a row
 * @param {object} request
 * @param {string} request.param
 * @param {object} response
 * @return {response} response
 */
function deleteFn (req, res) {
	var NAME = getName(req);
	var ADDRESS = getAddress(req);

	Member.remove({ name: NAME, address: ADDRESS }, respond);

	function respond (err, docs) {
		if (err) {
			return res.end();
		}

		if (!docs) {
			return res.status(404).send("Not found");
		}

		res.status(200).send(docs.result);
	}

}

function getName (req) {
	return req.params.NAME || req.body.name || "anonymous";
}

function getAddress (req) {
	return req.headers["x-forwarded-for"] || req.connection.remoteAddress;
}

function updateIpAddress (req, res) {
	return res.status(200).send();
}

function updateName (req, res) {
	return res.status(200).send();
}
<commit_msg>update or create doc<commit_after>"use strict";
var util = require("util");
var goose = require("../models/mongoose").instance;
var hostnameSchema = require("../models/schema-hostname.js");
var Member = goose.model("Member", hostnameSchema);

module.exports = {
	create: create,
	read: read,
	update: update,
	delete: deleteFn
};

/**
 * @description Create a new entry in the database
 * @param {object} request
 * @param {object} request.body
 * @param {string} request.body.name
 * @param {string} request.body.message
 * @param {string} request.body.address
 * @param {object} request.params
 * @param {string} request.params.NAME
 * @param {string} request.params.ADDRESS
 * @param {string} request.params.MESSAGE
 * @return {response} response
 */
function create (req, res) {

	var NAME = getName(req);
	var ADDRESS = getAddress(req);
	var MESSAGE = req.body.message || req.params.MESSAGE || "";
	console.log("name? ", NAME);
	console.log("address? ", ADDRESS);
	// console.log("gimme everything you got", req.connection);
	// console.log("port? ", req.connection.port);

	Member.create({
		name: NAME,
		address: ADDRESS,
		message: MESSAGE
	}, respond);

	// Member.findOneAndUpdate(
	// 	{ name: name, address: address, message: message },

	// 	{ new: true, upsert: true },

	// 	respond);

	function respond (err, doc) {
		if (err) {
			return res.status(409).json({ err: err });
		}

		console.log("new entry: ", doc);

		res.status(200).send(doc);
	}

}

/**
 * @description get an entry from the database
 * @param {object} request
 * @param {string} request.param
 * @param {object} response
 * @return {response} response
 */
function read (req, res) {

	var NAME = req.params.NAME;
	var ADDRESS = getAddress(req);

	if (NAME) {
		console.log("find single", NAME);
		Member.findOne({ name: NAME, address: ADDRESS }, respond);
	} else {
		// seems dangerous
		console.log("get all");
		Member.find({}, respond);
	}

	function respond (err, docs) {
		console.log("responding with docs", err, docs);
		if (err) {
			return res.send(err);
		}

		if (!docs || !Object.keys(docs).length) {
			return res.status(404).send("Not found");
		}

		res.status(200).send(docs);
	}

}

/**
 * @description Update an entry's IP address or name
 * @param {object} request
 * @param {string} request.param
 * @param {object} response
 * @return {response} response
 */
function update (req, res) {
	// IP will have ":"
	var IP = req.params.ADDRESS;
	var NAME = req.params.NAME;
	var MESSAGE = req.body.message || req.params.MESSAGE;

	Member.findOneAndUpdate(
		{ name: NAME, address: IP, message: MESSAGE, updated_at: Date.now() },

		{ new: true, upsert: true },

		respond);

	function respond (err, newDoc) {
		newDoc = newDoc || {};

		if (err) {
			return res.send(err);
		}

		if (!Object.keys(newDoc).length) {
			create(req, res);
			//return res.status(404).send("Not found");
		}

		res.status(200).send(newDoc);
	}

}

/**
 * @description delete a row
 * @param {object} request
 * @param {string} request.param
 * @param {object} response
 * @return {response} response
 */
function deleteFn (req, res) {
	var NAME = getName(req);
	var ADDRESS = getAddress(req);

	Member.remove({ name: NAME, address: ADDRESS }, respond);

	function respond (err, docs) {
		if (err) {
			return res.end();
		}

		if (!docs) {
			return res.status(404).send("Not found");
		}

		res.status(200).send(docs.result);
	}

}

function getName (req) {
	return req.params.NAME || req.body.name || "anonymous";
}

function getAddress (req) {
	return req.headers["x-forwarded-for"] || req.connection.remoteAddress;
}

function updateIpAddress (req, res) {
	return res.status(200).send();
}

function updateName (req, res) {
	return res.status(200).send();
}
<|endoftext|>


<commit_before>/**
 * Copyright 2015-2016, Google, Inc.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

const express = require('express');
require('express-csv');
const pwaLib = require('../../lib/pwa');
const libMetadata = require('../../lib/metadata');
const router = express.Router(); // eslint-disable-line new-cap
const verifyIdToken = require('../../lib/verify-id-token');
const bodyParser = require('body-parser');
const Pwa = require('../../models/pwa');
const CACHE_CONTROL_EXPIRES = 60 * 60 * 1; // 1 hour
const RSS = require('rss');

function getDate(date) {
  return new Date(date).toISOString().split('T')[0];
}

class CsvWriter {
  write(result, pwas) {
    const csv = [];
    pwas.forEach(pwa => {
      const created = getDate(pwa.created);
      const updated = getDate(pwa.updated);
      const csvLine = [];
      csvLine.push(pwa.id);
      csvLine.push(pwa.absoluteStartUrl);
      csvLine.push(pwa.manifestUrl);
      csvLine.push(pwa.lighthouseScore);
      csvLine.push(created);
      csvLine.push(updated);
      csv.push(csvLine);
    });
    result.setHeader('Content-Type', 'text/csv');
    csv.unshift(
      ['id', 'absoluteStartUrl', 'manifestUrl', 'lighthouseScore', 'created', 'updated']);
    result.csv(csv);
  }
}

class JsonWriter {
  write(result, pwas) {
    const pwaList = [];
    pwas.forEach(dbPwa => {
      const created = getDate(dbPwa.created);
      const updated = getDate(dbPwa.updated);
      const pwa = {};
      pwa.id = dbPwa.id;
      pwa.absoluteStartUrl = dbPwa.absoluteStartUrl;
      pwa.manifestUrl = dbPwa.manifestUrl;
      pwa.lighthouseScore = dbPwa.lighthouseScore;
      pwa.webPageTest = dbPwa.webPageTest;
      pwa.pageSpeed = dbPwa.pageSpeed;
      pwa.created = created;
      pwa.updated = updated;
      pwaList.push(pwa);
    });
    result.setHeader('Content-Type', 'application/json');
    result.json(pwaList);
  }
}

function render(res, view, options) {
  return new Promise((resolve, reject) => {
    res.render(view, options, (err, html) => {
      if (err) {
        console.log(err);
        reject(err);
      }
      resolve(html);
    });
  });
}

function renderOnePwaRss(pwa, req, res) {
  const url = req.originalUrl;
  const contentOnly = false || req.query.contentOnly;
  let arg = Object.assign(libMetadata.fromRequest(req, url), {
    pwa: pwa,
    title: 'PWA Directory: ' + pwa.name,
    description: 'PWA Directory: ' + pwa.name + ' - ' + pwa.description,
    backlink: true,
    contentOnly: contentOnly
  });
  return render(res, 'pwas/view-rss.hbs', arg);
}

async function asyncForEach(array, callback) {
  for (let index = 0; index < array.length; index++) {
    await callback(array[index], index, array);
  }
}

class RssWriter {
  write(req, res, pwas) {
    const feed = new RSS({
      /* eslint-disable camelcase */
      title: 'PWA Directory',
      description: 'A Directory of Progressive Web Apps',
      feed_url: 'https://pwa-directory.appspot.com/api/pwa/?format=rss',
      site_url: 'https://pwa-directory.appspot.com/',
      image_url: 'https://pwa-directory.appspot.com/favicons/android-chrome-144x144.png',
      pubDate: new Date(),
      custom_namespaces: {
        rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
        l: 'http://purl.org/rss/1.0/modules/link/',
        media: 'http://search.yahoo.com/mrss/',
        content: 'http://purl.org/rss/1.0/modules/content/'
      }
    });

    const start = async _ => {
      await asyncForEach(pwas, async pwa => {
        let html = await renderOnePwaRss(pwa, req, res);

        const customElements = [];
        customElements.push({'content:encoded': html});
        customElements.push({'l:link': {_attr: {'l:rel': 'http://purl.org/rss/1.0/modules/link/#alternate',
          'l:type': 'application/json',
          'rdf:resource': 'https://pwa-directory.appspot.com/api/pwa/' + pwa.id}}});
        if (pwa.iconUrl128) {
          customElements.push({'media:thumbnail': {_attr: {url: pwa.iconUrl128,
            height: '128', width: '128'}}});
        }

        feed.item({
          title: pwa.displayName,
          url: 'https://pwa-directory.appspot.com/pwas/' + pwa.id,
          description: html,
          guid: pwa.id,
          date: pwa.created,
          custom_elements: customElements
        });
      });
      res.setHeader('Content-Type', 'application/rss+xml');
      res.status(200).send(feed.xml());
    };
    start();
    /* eslint-enable camelcase */
  }
}

const csvWriter = new CsvWriter();
const jsonWriter = new JsonWriter();
const rssWriter = new RssWriter();

/**
 * GET /api/pwa
 *
  * Returns all PWAs as JSON, ?format=csv for CSV or ?format=rss for RSS feed
 */
router.get('/:id*?', (req, res) => {
  let format = req.query.format || 'json';
  let sort = req.query.sort || 'newest';
  let skip = parseInt(req.query.skip, 10);
  let limit = parseInt(req.query.limit, 10) || 100;
  res.setHeader('Cache-Control', 'public, max-age=' + CACHE_CONTROL_EXPIRES);

  let queryPromise = req.params.id ? pwaLib.find(req.params.id) : pwaLib.list(skip, limit, sort);
  queryPromise
  .then(result => {
    result = result.pwas ? result : {pwas: [result]};
    switch (format) {
      case 'csv': {
        csvWriter.write(res, result.pwas);
        break;
      }
      case 'rss': {
        rssWriter.write(req, res, result.pwas);
        break;
      }
      default: {
        jsonWriter.write(res, result.pwas);
      }
    }
  })
  .catch(err => {
    console.log(err);
    let code = err.code || 500;
    res.status(code);
    res.json(err);
  });
});

router.post('/add', bodyParser.json(), (req, res) => {
  const idToken = req.body.idToken;

  if (!idToken) {
    res.sendStatus(401, JSON.stringify({error: 'user not logged in'}));
    return;
  }

  const manifestUrl = req.body.manifestUrl;
  if (!manifestUrl) {
    res.sendStatus(400, JSON.stringify({error: 'no manifest provided'}));
    return;
  }

  (async () => {
    try {
      const pwa = new Pwa(manifestUrl);
      const user = await verifyIdToken.verifyIdToken(idToken);
      pwa.setUser(user);
      const savedPwa = await pwaLib.createOrUpdatePwa(pwa);
      res.json({
        id: savedPwa.id
      });
    } catch (e) {
      res.sendStatus(400, JSON.stringify({error: e}));
    }
  })();
});

module.exports = router;
<commit_msg>Adds URL verification<commit_after>/**
 * Copyright 2015-2016, Google, Inc.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

const express = require('express');
require('express-csv');
const pwaLib = require('../../lib/pwa');
const libMetadata = require('../../lib/metadata');
const router = express.Router(); // eslint-disable-line new-cap
const verifyIdToken = require('../../lib/verify-id-token');
const bodyParser = require('body-parser');
const Pwa = require('../../models/pwa');
const color = require('../../lib/color');
const CACHE_CONTROL_EXPIRES = 60 * 60 * 1; // 1 hour
const RSS = require('rss');
const {URL} = require('url');

function getDate(date) {
  return new Date(date).toISOString().split('T')[0];
}

class CsvWriter {
  write(result, pwas) {
    const csv = [];
    pwas.forEach(pwa => {
      const created = getDate(pwa.created);
      const updated = getDate(pwa.updated);
      const csvLine = [];
      csvLine.push(pwa.id);
      csvLine.push(pwa.absoluteStartUrl);
      csvLine.push(pwa.manifestUrl);
      csvLine.push(pwa.lighthouseScore);
      csvLine.push(created);
      csvLine.push(updated);
      csv.push(csvLine);
    });
    result.setHeader('Content-Type', 'text/csv');
    csv.unshift(
      ['id', 'absoluteStartUrl', 'manifestUrl', 'lighthouseScore', 'created', 'updated']);
    result.csv(csv);
  }
}

class JsonWriter {
  write(result, pwas) {
    const pwaList = [];
    pwas.forEach(dbPwa => {
      const created = getDate(dbPwa.created);
      const updated = getDate(dbPwa.updated);
      const pwa = {};
      pwa.id = dbPwa.id;
      pwa.absoluteStartUrl = dbPwa.absoluteStartUrl;
      pwa.manifestUrl = dbPwa.manifestUrl;
      pwa.lighthouseScore = dbPwa.lighthouseScore;
      pwa.webPageTest = dbPwa.webPageTest;
      pwa.pageSpeed = dbPwa.pageSpeed;
      pwa.created = created;
      pwa.updated = updated;
      pwaList.push(pwa);
    });
    result.setHeader('Content-Type', 'application/json');
    result.json(pwaList);
  }
}

function render(res, view, options) {
  return new Promise((resolve, reject) => {
    res.render(view, options, (err, html) => {
      if (err) {
        console.log(err);
        reject(err);
      }
      resolve(html);
    });
  });
}

function renderOnePwaRss(pwa, req, res) {
  const url = req.originalUrl;
  const contentOnly = false || req.query.contentOnly;
  let arg = Object.assign(libMetadata.fromRequest(req, url), {
    pwa: pwa,
    title: 'PWA Directory: ' + pwa.name,
    description: 'PWA Directory: ' + pwa.name + ' - ' + pwa.description,
    backlink: true,
    contentOnly: contentOnly
  });
  return render(res, 'pwas/view-rss.hbs', arg);
}

async function asyncForEach(array, callback) {
  for (let index = 0; index < array.length; index++) {
    await callback(array[index], index, array);
  }
}

class RssWriter {
  write(req, res, pwas) {
    const feed = new RSS({
      /* eslint-disable camelcase */
      title: 'PWA Directory',
      description: 'A Directory of Progressive Web Apps',
      feed_url: 'https://pwa-directory.appspot.com/api/pwa/?format=rss',
      site_url: 'https://pwa-directory.appspot.com/',
      image_url: 'https://pwa-directory.appspot.com/favicons/android-chrome-144x144.png',
      pubDate: new Date(),
      custom_namespaces: {
        rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
        l: 'http://purl.org/rss/1.0/modules/link/',
        media: 'http://search.yahoo.com/mrss/',
        content: 'http://purl.org/rss/1.0/modules/content/'
      }
    });

    const start = async _ => {
      await asyncForEach(pwas, async pwa => {
        let html = await renderOnePwaRss(pwa, req, res);

        const customElements = [];
        customElements.push({'content:encoded': html});
        customElements.push({'l:link': {_attr: {'l:rel': 'http://purl.org/rss/1.0/modules/link/#alternate',
          'l:type': 'application/json',
          'rdf:resource': 'https://pwa-directory.appspot.com/api/pwa/' + pwa.id}}});
        if (pwa.iconUrl128) {
          customElements.push({'media:thumbnail': {_attr: {url: pwa.iconUrl128,
            height: '128', width: '128'}}});
        }

        feed.item({
          title: pwa.displayName,
          url: 'https://pwa-directory.appspot.com/pwas/' + pwa.id,
          description: html,
          guid: pwa.id,
          date: pwa.created,
          custom_elements: customElements
        });
      });
      res.setHeader('Content-Type', 'application/rss+xml');
      res.status(200).send(feed.xml());
    };
    start();
    /* eslint-enable camelcase */
  }
}

const csvWriter = new CsvWriter();
const jsonWriter = new JsonWriter();
const rssWriter = new RssWriter();

/**
 * GET /api/pwa
 *
  * Returns all PWAs as JSON, ?format=csv for CSV or ?format=rss for RSS feed
 */
router.get('/:id*?', (req, res) => {
  let format = req.query.format || 'json';
  let sort = req.query.sort || 'newest';
  let skip = parseInt(req.query.skip, 10);
  let limit = parseInt(req.query.limit, 10) || 100;
  res.setHeader('Cache-Control', 'public, max-age=' + CACHE_CONTROL_EXPIRES);

  let queryPromise = req.params.id ? pwaLib.find(req.params.id) : pwaLib.list(skip, limit, sort);
  queryPromise
  .then(result => {
    result = result.pwas ? result : {pwas: [result]};
    switch (format) {
      case 'csv': {
        csvWriter.write(res, result.pwas);
        break;
      }
      case 'rss': {
        rssWriter.write(req, res, result.pwas);
        break;
      }
      default: {
        jsonWriter.write(res, result.pwas);
      }
    }
  })
  .catch(err => {
    console.log(err);
    let code = err.code || 500;
    res.status(code);
    res.json(err);
  });
});

router.post('/add', bodyParser.json(), (req, res) => {
  const idToken = req.body.idToken;

  if (!idToken) {
    res.status(400).send({error: 'user not logged in'});
    return;
  }

  const manifestUrl = req.body.manifestUrl;
  if (!manifestUrl) {
    res.status(400).send({error: 'no manifest provided'});
    return;
  }

  try {
    const url = new URL(manifestUrl);
    (async () => {
      try {
        const pwa = new Pwa(url.toString());
        const user = await verifyIdToken.verifyIdToken(idToken);
        pwa.setUser(user);
        const savedPwa = await pwaLib.createOrUpdatePwa(pwa);
        res.json({
          id: savedPwa.id,
          name: savedPwa.name,
          backgroundColor: savedPwa.backgroundColor,
          foregroundColor: color.bestContrastRatio('#FFFFFF', '#000000', savedPwa.backgroundColor)
        });
      } catch (e) {
        const message = e.message || e;
        res.status(400).json({error: message});
      }
    })();
  } catch (e) {
    res.status(400).send({error: 'manifestUrl is not an URL'});
  }
});

module.exports = router;
<|endoftext|>


<commit_before>function traverseDOM(node){
  var childLen=node.childNodes.length;
  if(childLen==0){
  }else{
    for(var i=0;i<childLen;i++){
      var childTmp=node.childNodes[i];
      console.log(childTmp);
      if(childTmp.nodeName=="DIV"){
        if(childTmp.style){
          var obj=window.getComputedStyle(childTmp);
          if(obj.hasOwnProperty("position")){
            if(obj["position"]=="fixed"){
              childTmp.style.display="none";
            }
            return;
          }
        }
      }
      traverseDOM(childTmp);
    }
  }
}
console.log(document.body);
traverseDOM(document);

<commit_msg>Update test.js<commit_after>function traverseDOM(node){
  var childLen=node.childNodes.length;
  if(childLen==0){
  }else{
    for(var i=0;i<childLen;i++){
      var childTmp=node.childNodes[i];
      console.log(childTmp);
      if(childTmp.nodeName=="DIV"){
        if(childTmp.style){
          var obj=window.getComputedStyle(childTmp);
          if(obj.hasOwnProperty("position")){
            if(obj["position"]=="fixed"){
              childTmp.style.display="none";
            }
            return;
          }
        }
      }
      traverseDOM(childTmp);
    }
  }
}
console.log(document.body);
traverseDOM(document.body);

<|endoftext|>


<commit_before>var secrets = require('../config/secrets');
var nodemailer = require("nodemailer");
var smtpTransport = nodemailer.createTransport("SMTP", {
    service: "Mailgun",
    auth: {
        user: secrets.mailgun.login,
        pass: secrets.mailgun.password
    }
    // service: "Sendgrid",
    // auth: {
    //     user: secrets.sendgrid.user,
    //     pass: secrets.sendgrid.password
    // }
});

/**
 * GET /contact
 * Contact form page.
 */

exports.getContact = function(req, res) {
  res.render('contact', {
    title: 'Contact'
  });
};

/**
 * POST /contact
 * Send a contact form via Nodemailer.
 * @param email
 * @param name
 * @param message
 */

exports.postContact = function(req, res) {
  req.assert('name', 'Name cannot be blank').notEmpty();
  req.assert('email', 'Email is not valid').isEmail();
  req.assert('message', 'Message cannot be blank').notEmpty();

  var errors = req.validationErrors();

  if (errors) {
    req.flash('errors', errors);
    return res.redirect('/contact');
  }

  var from = req.body.email;
  var name = req.body.name;
  var body = req.body.message;
  var to = 'you@email.com';
  var subject = 'API Example | Contact Form';

  var mailOptions = {
    to: to,
    from: from,
    subject: subject,
    text: body + '\n\n' + name
  };

  smtpTransport.sendMail(mailOptions, function(err) {
    if (err) {
      req.flash('errors', { msg: err.message });
      return res.redirect('/contact');
    }
    req.flash('success', { msg: 'Email has been sent successfully!' });
    res.redirect('/contact');
  });
};
<commit_msg>JavaScript style formatting. SendGrid as a default service for contact form.<commit_after>var secrets = require('../config/secrets');
var nodemailer = require("nodemailer");
var smtpTransport = nodemailer.createTransport('SMTP', {
//  service: 'Mailgun',
//  auth: {
//    user: secrets.mailgun.login,
//    pass: secrets.mailgun.password
//  }
  service: 'SendGrid',
  auth: {
       user: secrets.sendgrid.user,
       pass: secrets.sendgrid.password
  }
});

/**
 * GET /contact
 * Contact form page.
 */

exports.getContact = function(req, res) {
  res.render('contact', {
    title: 'Contact'
  });
};

/**
 * POST /contact
 * Send a contact form via Nodemailer.
 * @param email
 * @param name
 * @param message
 */

exports.postContact = function(req, res) {
  req.assert('name', 'Name cannot be blank').notEmpty();
  req.assert('email', 'Email is not valid').isEmail();
  req.assert('message', 'Message cannot be blank').notEmpty();

  var errors = req.validationErrors();

  if (errors) {
    req.flash('errors', errors);
    return res.redirect('/contact');
  }

  var from = req.body.email;
  var name = req.body.name;
  var body = req.body.message;
  var to = 'your@email.com';
  var subject = 'API Example | Contact Form';

  var mailOptions = {
    to: to,
    from: from,
    subject: subject,
    text: body + '\n\n' + name
  };

  smtpTransport.sendMail(mailOptions, function(err) {
    if (err) {
      req.flash('errors', { msg: err.message });
      return res.redirect('/contact');
    }
    req.flash('success', { msg: 'Email has been sent successfully!' });
    res.redirect('/contact');
  });
};
<|endoftext|>