<commit_before>
/**
 * Module dependencies
 */

var Stream = require('stream').Stream;
var fs = require('fs');

/**
 * expose
 * @ignore
 */

module.exports = exports = GridReadStream;

/**
 * GridReadStream
 *
 * @param {Grid} grid
 * @param {Object} options
 */

function GridReadStream (grid, options) {
  if (!(this instanceof GridReadStream))
    return new GridReadStream(grid, options);

  Stream.call(this);
  this.paused = false;
  this.readable = true;

  this._grid = grid;

  // a bit backwards compatible
  if (typeof options === 'string') {
    options = { filename: options };
  }
  this.options = options || {};
  if(options._id) {
    this.id = grid.tryParseObjectId(options._id);

    if(!this.id) {
      this.id = options._id;
    }
  }

  this.name = this.options.filename || '';
  this.mode = 'r';

  this.range = this.options.range || { startPos: 0, endPos: undefined };
  if (typeof(this.range.startPos) === 'undefined') {
    this.range.startPos = 0;
  }

  this._store = new grid.mongo.GridStore(grid.db, this.id || new grid.mongo.BSONPure.ObjectID, this.name, this.mode, this.options);
  // Workaround for Gridstore issue https://github.com/mongodb/node-mongodb-native/pull/930
  if (!this.id) {
    //var REFERENCE_BY_FILENAME = 0,
    this._store.referenceBy = 0;
  }

  var self = this;
  process.nextTick(function () {
    self._open();
  });
}

/**
 * Inherit from Stream
 * @ignore
 */

GridReadStream.prototype = { __proto__: Stream.prototype }

// public api

GridReadStream.prototype.readable;
GridReadStream.prototype.paused;

GridReadStream.prototype.setEncoding = fs.ReadStream.prototype.setEncoding;

/**
 * pause
 *
 * @api public
 */

GridReadStream.prototype.pause = function pause () {
  // Overridden when the GridStore opens.
  this.paused = true;
}

/**
 * resume
 *
 * @api public
 */

GridReadStream.prototype.resume = function resume () {
  // Overridden when the GridStore opens.
  this.paused = false;
}

/**
 * destroy
 *
 * @api public
 */

GridReadStream.prototype.destroy = function destroy () {
  // Overridden when the GridStore opens.
  this.readable = false;
}

// private api

GridReadStream.prototype._open = function _open () {
  var self = this;
  this._store.open(function (err) {
    if (err) return self._error(err);
    if (!self.readable) return;
    self._store.seek(self.range.startPos, function(err) {
      if (err) return self._error(err);
      self.emit('open');
      self._read();
    });
  });
}

GridReadStream.prototype._read = function _read () {
  //Don't check this.paused here -- paused or not, the stream needs to be prepared!
  if (!this.readable || this.reading) {
    return;
  }

  this.reading = true;

  var self = this;
  var stream = this._stream = this._store.stream();
  stream.paused = this.paused;

  var currentPos = self.range.startPos;
  var isGreatThanEndPos = false;

  stream.on('data', function (data) {
    if (typeof(self.range.endPos) !== 'undefined' && currentPos + data.length > self.range.endPos + 1) {
      isGreatThanEndPos = true;
      data = data.slice(0, self.range.endPos - currentPos + 1);
    }

    if (self._decoder) {
      var str = self._decoder.write(data);
      if (str.length) self.emit('data', str);
    } else {
      self.emit('data', data);
    }

    if (isGreatThanEndPos) {
      self.emit('end');
      self.destroy();
    }

    currentPos += data.length;
  });

  stream.on('end', function (data) {
    if (!isGreatThanEndPos) {
      self.emit('end', data);
    }
  });

  stream.on('error', function (data) {
    self._error(data);
  });

  stream.on('close', function (data) {
    self.emit('close', data);
  });

  this.pause = function () {
    // native doesn't always pause.
    // bypass its pause() method to hack it
    self.paused = stream.paused = true;
  }

  this.resume = function () {
    self.paused = false;
    stream.resume();
    self.readable = stream.readable;
  }

  this.destroy = function () {
    self.readable = false;
    stream.destroy();
  }
}

GridReadStream.prototype._error = function _error (err) {
  this.readable = false;
  this.emit('error', err);
}

<commit_msg>Let native stream implementation decide if stream can be paused<commit_after>
/**
 * Module dependencies
 */

var Stream = require('stream').Stream;
var fs = require('fs');

/**
 * expose
 * @ignore
 */

module.exports = exports = GridReadStream;

/**
 * GridReadStream
 *
 * @param {Grid} grid
 * @param {Object} options
 */

function GridReadStream (grid, options) {
  if (!(this instanceof GridReadStream))
    return new GridReadStream(grid, options);

  Stream.call(this);
  this.paused = false;
  this.readable = true;

  this._grid = grid;

  // a bit backwards compatible
  if (typeof options === 'string') {
    options = { filename: options };
  }
  this.options = options || {};
  if(options._id) {
    this.id = grid.tryParseObjectId(options._id);

    if(!this.id) {
      this.id = options._id;
    }
  }

  this.name = this.options.filename || '';
  this.mode = 'r';

  this.range = this.options.range || { startPos: 0, endPos: undefined };
  if (typeof(this.range.startPos) === 'undefined') {
    this.range.startPos = 0;
  }

  this._store = new grid.mongo.GridStore(grid.db, this.id || new grid.mongo.BSONPure.ObjectID, this.name, this.mode, this.options);
  // Workaround for Gridstore issue https://github.com/mongodb/node-mongodb-native/pull/930
  if (!this.id) {
    //var REFERENCE_BY_FILENAME = 0,
    this._store.referenceBy = 0;
  }

  var self = this;
  process.nextTick(function () {
    self._open();
  });
}

/**
 * Inherit from Stream
 * @ignore
 */

GridReadStream.prototype = { __proto__: Stream.prototype }

// public api

GridReadStream.prototype.readable;
GridReadStream.prototype.paused;

GridReadStream.prototype.setEncoding = fs.ReadStream.prototype.setEncoding;

/**
 * pause
 *
 * @api public
 */

GridReadStream.prototype.pause = function pause () {
  // Overridden when the GridStore opens.
  this.paused = true;
}

/**
 * resume
 *
 * @api public
 */

GridReadStream.prototype.resume = function resume () {
  // Overridden when the GridStore opens.
  this.paused = false;
}

/**
 * destroy
 *
 * @api public
 */

GridReadStream.prototype.destroy = function destroy () {
  // Overridden when the GridStore opens.
  this.readable = false;
}

// private api

GridReadStream.prototype._open = function _open () {
  var self = this;
  this._store.open(function (err) {
    if (err) return self._error(err);
    if (!self.readable) return;
    self._store.seek(self.range.startPos, function(err) {
      if (err) return self._error(err);
      self.emit('open');
      self._read();
    });
  });
}

GridReadStream.prototype._read = function _read () {
  //Don't check this.paused here -- paused or not, the stream needs to be prepared!
  if (!this.readable || this.reading) {
    return;
  }

  this.reading = true;

  var self = this;
  var stream = this._stream = this._store.stream();
  stream.paused = this.paused;

  var currentPos = self.range.startPos;
  var isGreatThanEndPos = false;

  stream.on('data', function (data) {
    if (typeof(self.range.endPos) !== 'undefined' && currentPos + data.length > self.range.endPos + 1) {
      isGreatThanEndPos = true;
      data = data.slice(0, self.range.endPos - currentPos + 1);
    }

    if (self._decoder) {
      var str = self._decoder.write(data);
      if (str.length) self.emit('data', str);
    } else {
      self.emit('data', data);
    }

    if (isGreatThanEndPos) {
      self.emit('end');
      self.destroy();
    }

    currentPos += data.length;
  });

  stream.on('end', function (data) {
    if (!isGreatThanEndPos) {
      self.emit('end', data);
    }
  });

  stream.on('error', function (data) {
    self._error(data);
  });

  stream.on('close', function (data) {
    self.emit('close', data);
  });

  this.pause = function () {
    stream.pause();
    self.paused = stream.paused;
  }

  this.resume = function () {
    self.paused = false;
    stream.resume();
    self.readable = stream.readable;
  }

  this.destroy = function () {
    self.readable = false;
    stream.destroy();
  }
}

GridReadStream.prototype._error = function _error (err) {
  this.readable = false;
  this.emit('error', err);
}

<|endoftext|>


<commit_before>module.exports = [
  { prop: 'animation', value: 'none 0s ease 0s 1 normal none running' },
  { prop: 'backface-visibility', value: 'visible' },
  { prop: 'background', value: 'transparent none repeat 0% 0% / auto auto padding-box border-box scroll' },
  { prop: 'border', value: 'medium none currentColor' },
  { prop: 'border-radius', value: '0' },
  { prop: 'border-collapse', value: 'separate' },
  { prop: 'border-image', value: 'none' },
  { prop: 'border-spacing', value: '0' },
  { prop: 'bottom', value: 'auto' },
  { prop: 'box-shadow', value: 'none' },
  { prop: 'box-sizing', value: 'content-box' },
  { prop: 'caption-side', value: 'top' },
  { prop: 'clear', value: 'none' },
  { prop: 'clip', value: 'auto' },
  { prop: 'color', value: 'inherit' },
  { prop: 'columns', value: 'auto' },
  { prop: 'column-count', value: 'auto' },
  { prop: 'column-fill', value: 'balance' },
  { prop: 'column-gap', value: 'normal' },
  { prop: 'column-rule', value: 'medium none currentColor' },
  { prop: 'column-rule-color', value: 'currentColor' },
  { prop: 'column-rule-style', value: 'none' },
  { prop: 'column-rule-width', value: 'none' },
  { prop: 'column-span', value: '1' },
  { prop: 'column-width', value: 'auto' },
  { prop: 'content', value: 'normal' },
  { prop: 'counter-increment', value: 'none' },
  { prop: 'counter-reset', value: 'none' },
  { prop: 'cursor', value: 'auto' },
  { prop: 'direction', value: 'ltr' },
  { prop: 'display', value: 'inline' },
  { prop: 'empty-cells', value: 'show' },
  { prop: 'float', value: 'none' },
  { prop:  'font',
    value: 'normal normal normal normal medium normal sans-serif' },
  { prop: 'height', value: 'auto' },
  { prop: 'hyphens', value: 'none' },
  { prop: 'left', value: 'auto' },
  { prop: 'letter-spacing', value: 'normal' },
  { prop: 'line-height', value: 'normal' },
  { prop: 'list-style', value: 'none' },
  { prop: 'list-style-image', value: 'none' },
  { prop: 'list-style-position', value: 'outside' },
  { prop: 'list-style-type', value: 'disc' },
  { prop: 'margin', value: '0' },
  { prop: 'max-height', value: 'none' },
  { prop: 'max-width', value: 'none' },
  { prop: 'min-height', value: '0' },
  { prop: 'min-width', value: '0' },
  { prop: 'opacity', value: '1' },
  { prop: 'orphans', value: '0' },
  { prop: 'outline', value: '0' },
  { prop: 'outline-color', value: 'invert' },
  { prop: 'outline-style', value: 'none' },
  { prop: 'outline-width', value: 'medium' },
  { prop: 'overflow', value: 'visible' },
  { prop: 'overflow-x', value: 'visible' },
  { prop: 'overflow-y', value: 'visible' },
  { prop: 'padding', value: '0' },
  { prop: 'page-break-after', value: 'auto' },
  { prop: 'page-break-before', value: 'auto' },
  { prop: 'page-break-inside', value: 'auto' },
  { prop: 'perspective', value: 'none' },
  { prop: 'perspective-origin', value: '50% 50%' },
  { prop: 'position', value: 'static' },
  { prop: 'right', value: 'auto' },
  { prop: 'tab-size', value: '8' },
  { prop: 'table-layout', value: 'auto' },
  { prop: 'text-align', value: 'inherit' },
  { prop: 'text-align-last', value: 'auto' },
  { prop: 'text-decoration', value: 'none' },
  { prop: 'text-decoration-color', value: 'inherit' },
  { prop: 'text-decoration-line', value: 'none' },
  { prop: 'text-decoration-style', value: 'solid' },
  { prop: 'text-indent', value: '0' },
  { prop: 'text-shadow', value: 'none' },
  { prop: 'text-transform', value: 'none' },
  { prop: 'top', value: 'auto' },
  { prop: 'transform', value: 'none' },
  { prop: 'transform-style', value: 'flat' },
  { prop: 'transition', value: 'none' },
  { prop: 'transition-delay', value: '0s' },
  { prop: 'transition-duration', value: '0s' },
  { prop: 'transition-property', value: 'none' },
  { prop: 'transition-timing-function', value: 'ease' },
  { prop: 'unicode-bidi', value: 'normal' },
  { prop: 'vertical-align', value: 'baseline' },
  { prop: 'visibility', value: 'visible' },
  { prop: 'white-space', value: 'normal' },
  { prop: 'widows', value: '0' },
  { prop: 'width', value: 'auto' },
  { prop: 'word-spacing', value: 'normal' },
  { prop: 'z-index', value: 'auto' }
];
<commit_msg>Compress column-rule, list-style, outline, text-decoration,  transition<commit_after>module.exports = [
  { prop: 'animation', value: 'none 0s ease 0s 1 normal none running' },
  { prop: 'backface-visibility', value: 'visible' },
  { prop: 'background', value: 'transparent none repeat 0% 0% / auto auto padding-box border-box scroll' },
  { prop: 'border', value: 'medium none currentColor' },
  { prop: 'border-radius', value: '0' },
  { prop: 'border-collapse', value: 'separate' },
  { prop: 'border-image', value: 'none' },
  { prop: 'border-spacing', value: '0' },
  { prop: 'bottom', value: 'auto' },
  { prop: 'box-shadow', value: 'none' },
  { prop: 'box-sizing', value: 'content-box' },
  { prop: 'caption-side', value: 'top' },
  { prop: 'clear', value: 'none' },
  { prop: 'clip', value: 'auto' },
  { prop: 'color', value: 'inherit' },
  { prop: 'columns', value: 'auto' },
  { prop: 'column-count', value: 'auto' },
  { prop: 'column-fill', value: 'balance' },
  { prop: 'column-gap', value: 'normal' },
  { prop: 'column-rule', value: 'medium none currentColor' },
  { prop: 'column-span', value: '1' },
  { prop: 'column-width', value: 'auto' },
  { prop: 'content', value: 'normal' },
  { prop: 'counter-increment', value: 'none' },
  { prop: 'counter-reset', value: 'none' },
  { prop: 'cursor', value: 'auto' },
  { prop: 'direction', value: 'ltr' },
  { prop: 'display', value: 'inline' },
  { prop: 'empty-cells', value: 'show' },
  { prop: 'float', value: 'none' },
  { prop:  'font',
    value: 'normal normal normal normal medium normal sans-serif' },
  { prop: 'height', value: 'auto' },
  { prop: 'hyphens', value: 'none' },
  { prop: 'left', value: 'auto' },
  { prop: 'letter-spacing', value: 'normal' },
  { prop: 'line-height', value: 'normal' },
  { prop: 'list-style', value: 'disc outside none' },
  { prop: 'margin', value: '0' },
  { prop: 'max-height', value: 'none' },
  { prop: 'max-width', value: 'none' },
  { prop: 'min-height', value: '0' },
  { prop: 'min-width', value: '0' },
  { prop: 'opacity', value: '1' },
  { prop: 'orphans', value: '0' },
  { prop: 'outline', value: 'medium none invert' },
  { prop: 'overflow', value: 'visible' },
  { prop: 'overflow-x', value: 'visible' },
  { prop: 'overflow-y', value: 'visible' },
  { prop: 'padding', value: '0' },
  { prop: 'page-break-after', value: 'auto' },
  { prop: 'page-break-before', value: 'auto' },
  { prop: 'page-break-inside', value: 'auto' },
  { prop: 'perspective', value: 'none' },
  { prop: 'perspective-origin', value: '50% 50%' },
  { prop: 'position', value: 'static' },
  { prop: 'right', value: 'auto' },
  { prop: 'tab-size', value: '8' },
  { prop: 'table-layout', value: 'auto' },
  { prop: 'text-align', value: 'inherit' },
  { prop: 'text-align-last', value: 'auto' },
  { prop: 'text-decoration', value: 'none solid currentColor' },
  { prop: 'text-indent', value: '0' },
  { prop: 'text-shadow', value: 'none' },
  { prop: 'text-transform', value: 'none' },
  { prop: 'top', value: 'auto' },
  { prop: 'transform', value: 'none' },
  { prop: 'transform-style', value: 'flat' },
  { prop: 'transition', value: 'none' },
  { prop: 'unicode-bidi', value: 'normal' },
  { prop: 'vertical-align', value: 'baseline' },
  { prop: 'visibility', value: 'visible' },
  { prop: 'white-space', value: 'normal' },
  { prop: 'widows', value: '0' },
  { prop: 'width', value: 'auto' },
  { prop: 'word-spacing', value: 'normal' },
  { prop: 'z-index', value: 'auto' }
];
<|endoftext|>


<commit_before>var request = require('../request');
var redis = require('../redis');
var xmlParser = require('xml2json');
var util = require('util');
var config = require('../../config/weixin');

function Weixin(id) {
  this.id = id;
}

Weixin.prototype.getData = function*() {
  var rss = {};
  //list-cache
  var listCacheId = util.format(config.listCachePolicy.cacheId, this.id);
  var listCache =
    yield redis.get(listCacheId);
  if (config.cacheEnable && listCache && listCache.items)
    return JSON.parse(listCache);
  //list-collect
  var apiUrl = util.format(config.api.getList, this.id, new Date().getTime());
  rss.items = yield this.getListData(apiUrl);
  for (var i = 0, l = rss.items.length; i < l; i++) {
    var item = rss.items[i];
    //content-cache
    var contentCacheId = util.format(config.contentCachePolicy.cacheId, item.docid);
    var content =
      yield redis.get(contentCacheId);
    if (!config.cacheEnable || !content) {
      //content-collect
      content = yield this.getContentData(item);
      if (content)
        yield redis.setex(contentCacheId, config.contentCachePolicy.lifeTime, content);
    }
    item.fullContent = content;
  };
  if (rss.items[0]) {
    var recentItem = rss.items[0];
    rss.id = this.id;
    rss.url = apiUrl;
    rss.title = recentItem.sourcename;
    rss.updated = recentItem.date;
    rss.author = recentItem.sourcename;
  }
  yield redis.setex(listCacheId, config.listCachePolicy.lifeTime, JSON.stringify(rss));
  return rss;
};

Weixin.prototype.getListData = function*(apiUrl) {
  var items = [];
  var cookie = yield this.getRandomCookie();
  if (!cookie) {
    var cookies = yield this.getCookies();
    yield this.saveCookies(cookies);
    cookie = cookies[0];
  }
  var response =
    yield request({
      url: apiUrl,
      headers : this.getRequestHeaders(cookie)
    });
  if (response.error)
    return {
      error: response.error
    };
  if (response.statusCode != 200)
    return {
      error: 'response statusCode: ' + response.statusCode
    }
  var body = response.body;
  var leftLimit = body.indexOf('({');
  var rightLimit = body.indexOf('})');
  var dataJson = JSON.parse(body.slice(leftLimit + 1, rightLimit + 1));
  dataJson.items.forEach(function(item) {
    var itemJson = JSON.parse(xmlParser.toJson(item));
    var _item = itemJson.DOCUMENT.item.display;
    items.push(_item);
  });
  return items;
}

Weixin.prototype.getContentData = function*(item) {
    var result =
      yield request({
        url: item.url
      });
    if (!result.error && result.statusCode == 200) {
      var match = result.body.match(/id=\"js_content\">(.*)<\/div>/);
      if (match && match[1]) {
        content = match[1].replace(/data-src/g, 'src');
        return content;
      }
    }
}

Weixin.prototype.getCookies = function*() {
  var cookies = [];
  for (var i = 0, l = config.cookieNum; i < l; i++) {
    var cookie = yield this.getCookie();
    if (cookie)
      cookies.push(cookie);
  };
  return cookies;
}

Weixin.prototype.saveCookies = function*(cookies) {
    yield redis.sadd(config.cookieCachePolicy.cacheId, cookies);
    yield redis.expire(config.cookieCachePolicy.cacheId, config.cookieCachePolicy.lifeTime);
}

Weixin.prototype.getCookie = function*() {
  var headerCookie = 'ABTEST=0|1430540424|v1; SNUID=A596C73147425256E3BB03EE47618F92; IPLOC=CN4401; SUID=E2D081776A20900A0000000055445088; SUID=E2D081776914920A000000005544508B; SUV=00E979B57781D0E25544508BAF939158; SUIR=1430540428; PHPSESSID=9it6n9jmn65099uu6lgg2d0go5';
  var result = yield request({
    url: util.format(config.api.getCookie, Math.random().toString(36).substr(2)),
    headers: this.getRequestHeaders(headerCookie)
  });
  if (!result.error && result.statusCode == 200) {
    var cookieStr = result.headers['set-cookie'].join(';');
    console.log(cookieStr);
    var SNUID = cookieStr.match(/(SNUID=\S+?);/)[1];
    var SUID = cookieStr.match(/(SUID=\S+?);/)[1];
    var SUV = 'SUV=' + ((new Date()).getTime())*1000+Math.round(Math.random()*1000);
    var cookie = [SNUID, SUID, SUV].join(';');
    return cookie;
  }
}

Weixin.prototype.getRandomCookie = function*() {
  return yield redis.srandmember(config.cookieCachePolicy.cacheId);
}

Weixin.prototype.getRequestHeaders = function(cookie) {
  return {
    'Accept': '*/*',
    'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6',
    'Cache-Control': 'max-age=0',
    'Connection': 'keep-alive',
    'Cookie': cookie,
    'Host': 'weixin.sogou.com',
    'Referer': 'http://weixin.sogou.com/gzh?openid=oIWsFtzoNLI6-6I8GNfFgODdmXAQ',
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36'
  }
}

module.exports = Weixin;<commit_msg>cookie-pool<commit_after>var request = require('../request');
var redis = require('../redis');
var xmlParser = require('xml2json');
var util = require('util');
var config = require('../../config/weixin');

function Weixin(id) {
  this.id = id;
}

Weixin.prototype.getData = function*() {
  var rss = {};
  //list-cache
  var listCacheId = util.format(config.listCachePolicy.cacheId, this.id);
  var listCache =
    yield redis.get(listCacheId);
  if (config.cacheEnable && listCache && listCache.items)
    return JSON.parse(listCache);
  //list-collect
  var apiUrl = util.format(config.api.getList, this.id, new Date().getTime());
  rss.items = yield this.getListData(apiUrl);
  for (var i = 0, l = rss.items.length; i < l; i++) {
    var item = rss.items[i];
    //content-cache
    var contentCacheId = util.format(config.contentCachePolicy.cacheId, item.docid);
    var content =
      yield redis.get(contentCacheId);
    if (!config.cacheEnable || !content) {
      //content-collect
      content = yield this.getContentData(item);
      if (content)
        yield redis.setex(contentCacheId, config.contentCachePolicy.lifeTime, content);
    }
    item.fullContent = content;
  };
  if (rss.items[0]) {
    var recentItem = rss.items[0];
    rss.id = this.id;
    rss.url = apiUrl;
    rss.title = recentItem.sourcename;
    rss.updated = recentItem.date;
    rss.author = recentItem.sourcename;
  }
  yield redis.setex(listCacheId, config.listCachePolicy.lifeTime, JSON.stringify(rss));
  return rss;
};

Weixin.prototype.getListData = function*(apiUrl) {
  var items = [];
  var cookie = yield this.getRandomCookie();
  if (!cookie) {
    var cookies = yield this.getCookies();
    yield this.saveCookies(cookies);
    cookie = cookies[0];
  }
  var response =
    yield request({
      url: apiUrl,
      headers : this.getRequestHeaders(cookie)
    });
  if (response.error)
    return {
      error: response.error
    };
  if (response.statusCode != 200)
    return {
      error: 'response statusCode: ' + response.statusCode
    }
  var body = response.body;
  var leftLimit = body.indexOf('({');
  var rightLimit = body.indexOf('})');
  var dataJson = JSON.parse(body.slice(leftLimit + 1, rightLimit + 1));
  dataJson.items.forEach(function(item) {
    var itemJson = JSON.parse(xmlParser.toJson(item));
    var _item = itemJson.DOCUMENT.item.display;
    items.push(_item);
  });
  return items;
}

Weixin.prototype.getContentData = function*(item) {
    var result =
      yield request({
        url: item.url
      });
    if (!result.error && result.statusCode == 200) {
      var match = result.body.match(/id=\"js_content\">(.*)<\/div>/);
      if (match && match[1]) {
        content = match[1].replace(/data-src/g, 'src');
        return content;
      }
    }
}

Weixin.prototype.getCookies = function*() {
  var cookies = [];
  for (var i = 0, l = config.cookieNum; i < l; i++) {
    var cookie = yield this.getCookie();
    if (cookie)
      cookies.push(cookie);
  };
  return cookies;
}

Weixin.prototype.saveCookies = function*(cookies) {
    yield redis.sadd(config.cookieCachePolicy.cacheId, cookies);
    yield redis.expire(config.cookieCachePolicy.cacheId, config.cookieCachePolicy.lifeTime);
}

Weixin.prototype.getCookie = function*() {
  var result = yield request({
    url: util.format(config.api.getCookie, Math.random().toString(36).substr(2))
  });
  if (!result.error && result.statusCode == 200) {
    var cookieStr = result.headers['set-cookie'].join(';');
    console.log(cookieStr);
    var SNUID = cookieStr.match(/(SNUID=\S+?);/);
    var SUID = cookieStr.match(/(SUID=\S+?);/)[1];
    if (SNUID) {
      SNUID = SNUID[1];
      var SUV = 'SUV=' + ((new Date()).getTime())*1000+Math.round(Math.random()*1000);
      var cookie = [SNUID, SUID, SUV].join(';');
      return cookie;
    }
  }
}

Weixin.prototype.getRandomCookie = function*() {
  return yield redis.srandmember(config.cookieCachePolicy.cacheId);
}

Weixin.prototype.getRequestHeaders = function(cookie) {
  return {
    'Accept': '*/*',
    'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6',
    'Cache-Control': 'max-age=0',
    'Connection': 'keep-alive',
    'Cookie': cookie,
    'Host': 'weixin.sogou.com',
    'Referer': 'http://weixin.sogou.com/gzh?openid=oIWsFtzoNLI6-6I8GNfFgODdmXAQ',
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36'
  }
}

module.exports = Weixin;<|endoftext|>


<commit_before>/**
 * Satellizer 0.0.1
 * (c) 2014 Sahat Yalkabov <sahat@me.com>
 * License: MIT
 */

(function(window, angular, undefined) {
  'use strict';

  angular.module('Satellizer', [])
    .service('Utils', Utils)
    .factory('OAuth2', Oauth2)
    .factory('OAuth1', Oauth1)
    .factory('Popup', Popup)
    .provider('$auth', $auth)
    .config(httpInterceptor)
    .run(onRun);

  var config = {
    logoutRedirect: '/',
    loginRedirect: '/',
    loginUrl: '/auth/login',
    signupUrl: '/auth/signup',
    signupRedirect: '/login',
    loginRoute: '/login',
    signupRoute: '/signup',
    user: 'currentUser'
  };

  var providers = {
    google: {
      url: '/auth/google',
      authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth',
      redirectUri: window.location.origin,
      scope: 'openid profile email',
      requiredUrlParams: ['scope'],
      optionalUrlParams: ['display'],
      display: 'popup',
      type: 'oauth2',
      popupOptions: {
        width: 452,
        height: 633
      }
    },
    facebook: {
      url: '/auth/facebook',
      authorizationEndpoint: 'https://www.facebook.com/dialog/oauth',
      redirectUri: window.location.origin,
      scope: 'email',
      requiredUrlParams: ['display'],
      display: 'popup',
      type: 'oauth2',
      popupOptions: {
        width: 481,
        height: 269
      }
    },
    linkedin: {
      url: '/auth/linkedin',
      authorizationEndpoint: 'https://www.linkedin.com/uas/oauth2/authorization',
      redirectUri: window.location.origin,
      requiredUrlParams: ['state'],
      state: 'STATE',
      type: 'oauth2'
    },
    twitter: {
      url: '/auth/twitter',
      authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate',
      type: 'oauth1'
    }
  };

  function $auth() {

    this.config = config;

    this.setProvider = function(params) {
      angular.extend(providers[params.name], params);
    };

    this.addProvider = function(params) {
      providers[params.name] = {};
      angular.extend(providers[params.name], params);
    };

    this.$get = function(OAuth1, OAuth2, $http, $location, $rootScope, $q, $window) {

      var $auth = {};

      $auth.authenticate = function(providerName) {
        var deferred = $q.defer();

        var provider = (providers[providerName].type === 'oauth1') ? OAuth1 : OAuth2;

        provider.open(providers[providerName]).then(function(token) {
          var payload = JSON.parse($window.atob(token.split('.')[1]));
          $window.localStorage.jwtToken = token;
          $rootScope[config.user] = payload.user;
          $location.path(config.loginRedirect);
          deferred.resolve();
        });

        return deferred.promise;
      };

      $auth.login = function(user) {
        if (!user) {
          throw new Error('You must provide a user object.');
        }

        var deferred = $q.defer();

        $http.post(config.loginUrl, user)
          .success(function(data) {
            var payload = JSON.parse($window.atob(data.token.split('.')[1]));
            $rootScope[config.user] = payload.user;
            $location.path(config.loginRedirect);
            $window.localStorage.jwtToken = data.token;
            deferred.resolve(payload.user);
          })
          .error(function(error) {
            deferred.reject(error);
          });

        return deferred.promise;
      };

      $auth.signup = function(user) {
        $http.post(config.signupUrl, user).then(function() {
          $location.path(config.signupRedirect);
        });
      };

      $auth.logout = function() {
        delete $rootScope[config.user];
        delete $window.localStorage.jwtToken;
        $location.path(config.logoutRedirect);
      };

      $auth.isAuthenticated = function() {
        return Boolean($rootScope.currentUser);
      };

      return $auth;
    };

  }

  function Popup($q, $interval, $window) {
    var popupWindow = null;
    var polling = null;

    var popup = {};

    popup.open = function(url, options) {
      var deferred = $q.defer();
      var optionsString = popup.stringifyOptions(popup.prepareOptions(options));

      popupWindow = $window.open(url, 'Satellizer', optionsString);
      popupWindow.focus();

      this.postMessageHandler(deferred);
      this.pollPopup(deferred);

      return deferred.promise;
    };

    popup.pollPopup = function(deferred) {
      polling = $interval(function() {
        if (popupWindow.closed) {
          $interval.cancel(polling);
          deferred.reject('Popup was closed by the user.');
        }
      }, 35);
    };

    popup.postMessageHandler = function(deferred) {
      $window.addEventListener('message', function(event) {
        if (event.origin === $window.location.origin) {
          deferred.resolve(event.data);
        }
      }, false);
    };

    popup.prepareOptions = function(options) {
      var defaults = {
        width: options.width || 500,
        height: options.height || 500,
        left: $window.screenX + (($window.outerWidth - options.width) / 2),
        top: $window.screenY + (($window.outerHeight - options.height) / 2.5)
      };
      return angular.extend(defaults, options);
    };

    popup.stringifyOptions = function(options) {
      var parts = [];
      angular.forEach(options, function(value, key) {
        parts.push(key + '=' + value);
      });
      return parts.join(',');
    };

    return popup;
  }

  function Oauth1($q, $http, Popup) {
    var defaults = {
      url: null,
      name: null,
      popupOptions: null,
      authorizationEndpoint: null
    };

    var oauth1 = {};

    oauth1.tokenRequest = function() {
      return $http.post(defaults.url);
    };

    oauth1.open = function(options) {
      angular.extend(defaults, options);

      var deferred = $q.defer();

      Popup.open(defaults.url).then(function(oauthData) {
        this.exchangeForToken(oauthData).then(function(response) {
          deferred.resolve(response.data);
        });
      });

      return deferred.promise;
    };

    oauth1.exchangeForToken = function(oauthData) {
      return $http.get(defaults.url, { params: oauthData });
    };

    return oauth1;
  }

  function Oauth2($q, $http, Utils, Popup) {
    var defaults = {
      url: null,
      name: null,
      scope: null,
      clientId: null,
      redirectUri: null,
      popupOptions: null,
      requiredUrlParams: null,
      optionalUrlParams: null,
      authorizationEndpoint: null,
      responseType: 'code',
      defaultUrlParams: ['response_type', 'client_id', 'redirect_uri']
    };

    var oauth2 = {};

    oauth2.open = function(options) {
      angular.extend(defaults, options);
      var deferred = $q.defer();
      var url = oauth2.buildUrl();

      Popup.open(url, defaults.popupOptions).then(function(oauthData) {
        oauth2.exchangeForToken(oauthData).then(function(response) {
          deferred.resolve(response.data);
        });
      });

      return deferred.promise;
    };

    oauth2.exchangeForToken = function(oauthData) {
      return $http.post(defaults.url, {
        code: oauthData.code,
        clientId: defaults.clientId,
        redirectUri: defaults.redirectUri
      });
    };

    oauth2.buildUrl = function() {
      var baseUrl = defaults.authorizationEndpoint;
      var qs = oauth2.buildQueryString();
      return [baseUrl, qs].join('?');
    };

    oauth2.buildQueryString = function() {
      var keyValuePairs = [];
      var urlParams = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams'];

      angular.forEach(urlParams, function(params) {
        angular.forEach(defaults[params], function(paramName) {
          var camelizedName = Utils.camelCase(paramName);
          var paramValue = defaults[camelizedName];
          keyValuePairs.push([paramName, encodeURIComponent(paramValue)]);
        });
      });

      return keyValuePairs.map(function(pair) {
        return pair.join('=');
      }).join('&');
    };

    return oauth2;
  }

  function httpInterceptor($httpProvider) {
    $httpProvider.interceptors.push(function($q, $window, $location) {
      return {
        request: function(config) {
          if ($window.localStorage.jwtToken) {
            config.headers.Authorization = 'Bearer ' + $window.localStorage.jwtToken;
          }
          return config;
        },
        responseError: function(response) {
          if (response.status === 401 || response.status === 403) {
            $location.path('/login');
          }
          return $q.reject(response);
        }
      };
    });
  }

  function onRun($rootScope, $window, $location, Utils) {
    var token = $window.localStorage.jwtToken;
    if (token) {
      var payload = JSON.parse($window.atob(token.split('.')[1]));
      $rootScope.currentUser = payload.user;
    }

    var params = $window.location.search.substring(1);
    var qs = Utils.parseQueryString(params);
    if ($window.opener && $window.opener.location.origin === $window.location.origin) {
      if (qs.oauth_token && qs.oauth_verifier) {
        $window.opener.postMessage({ oauth_token: qs.oauth_token, oauth_verifier: qs.oauth_verifier }, '*');
      } else if (qs.code) {
        $window.opener.postMessage({ code: qs.code }, '*');
      }
      $window.close();
    }

    $rootScope.$on('$routeChangeStart', function(event, current) {
      if ($rootScope[config.user] &&
        (current.originalPath === '/login' || current.originalPath === '/signup')) {
        $location.path('/');
      }
      if (current.authenticated && !$rootScope[config.user]) {
        $location.path('/login');
      }
    });
  }

  function Utils() {
    this.camelCase = function(name) {
      return name.replace(/([\:\-\_]+(.))/g, function(_, separator, letter, offset) {
        return offset ? letter.toUpperCase() : letter;
      });
    };
    this.parseQueryString = function(keyValue) {
      var obj = { }, key, value;
      angular.forEach((keyValue || '').split('&'), function(keyValue) {
        if (keyValue) {
          value = keyValue.split('=');
          key = decodeURIComponent(value[0]);
          obj[key] = angular.isDefined(value[1]) ? decodeURIComponent(value[1]) : true;
        }
      });
      return obj;
    };
  }

})(window, window.angular);

<commit_msg>refactored $auth.login into a separate Local factory<commit_after>/**
 * Satellizer 0.0.1
 * (c) 2014 Sahat Yalkabov <sahat@me.com>
 * License: MIT
 */

(function(window, angular, undefined) {
  'use strict';

  angular.module('Satellizer', [])
    .service('Utils', Utils)
    .factory('Local', Local)
    .factory('Oauth2', Oauth2)
    .factory('Oauth1', Oauth1)
    .factory('Popup', Popup)
    .provider('$auth', $auth)
    .config(httpInterceptor)
    .run(onRun);

  var config = {
    logoutRedirect: '/',
    loginRedirect: '/',
    loginUrl: '/auth/login',
    signupUrl: '/auth/signup',
    signupRedirect: '/login',
    loginRoute: '/login',
    signupRoute: '/signup',
    user: 'currentUser'
  };

  var providers = {
    google: {
      url: '/auth/google',
      authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth',
      redirectUri: window.location.origin,
      scope: 'openid profile email',
      requiredUrlParams: ['scope'],
      optionalUrlParams: ['display'],
      display: 'popup',
      type: 'oauth2',
      popupOptions: {
        width: 452,
        height: 633
      }
    },
    facebook: {
      url: '/auth/facebook',
      authorizationEndpoint: 'https://www.facebook.com/dialog/oauth',
      redirectUri: window.location.origin,
      scope: 'email',
      requiredUrlParams: ['display'],
      display: 'popup',
      type: 'oauth2',
      popupOptions: {
        width: 481,
        height: 269
      }
    },
    linkedin: {
      url: '/auth/linkedin',
      authorizationEndpoint: 'https://www.linkedin.com/uas/oauth2/authorization',
      redirectUri: window.location.origin,
      requiredUrlParams: ['state'],
      state: 'STATE',
      type: 'oauth2'
    },
    twitter: {
      url: '/auth/twitter',
      authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate',
      type: 'oauth1'
    }
  };

  function $auth() {

    this.config = config;

    this.setProvider = function(params) {
      angular.extend(providers[params.name], params);
    };

    this.addProvider = function(params) {
      providers[params.name] = {};
      angular.extend(providers[params.name], params);
    };

    this.$get = function(Oauth1, Oauth2, Local, $http, $location, $rootScope, $q, $window) {

      var $auth = {};

      $auth.authenticate = function(providerName) {
        var deferred = $q.defer();
        var provider = (providers[providerName].type === 'oauth1') ? Oauth1 : Oauth2;
        provider.open(providers[providerName]).then(function(token) {
          var payload = JSON.parse($window.atob(token.split('.')[1]));
          $window.localStorage.jwtToken = token;
          $rootScope[config.user] = payload.user;
          $location.path(config.loginRedirect);
          deferred.resolve();
        });
        return deferred.promise;
      };

      $auth.login = function(user) {
        Local.login(user);
      };

      $auth.signup = function(user) {
        $http.post(config.signupUrl, user).then(function() {
          $location.path(config.signupRedirect);
        });
      };

      $auth.logout = function() {
        delete $rootScope[config.user];
        delete $window.localStorage.jwtToken;
        $location.path(config.logoutRedirect);
      };

      $auth.isAuthenticated = function() {
        return Boolean($rootScope.currentUser);
      };

      return $auth;
    };

  }

  function Local($q, $http, $window, $rootScope, $location) {

    var local = {};

    local.login = function(user) {
      var deferred = $q.defer();
      $http.post(config.loginUrl, user)
        .success(function(data) {
          var payload = JSON.parse($window.atob(data.token.split('.')[1]));
          $rootScope[config.user] = payload.user;
          $location.path(config.loginRedirect);
          $window.localStorage.jwtToken = data.token;
          deferred.resolve(payload.user);
        })
        .error(function(error) {
          deferred.reject(error);
        });
      return deferred.promise;
    };

    return local;
  }

  function Popup($q, $interval, $window) {
    var popupWindow = null;
    var polling = null;

    var popup = {};

    popup.open = function(url, options) {
      var deferred = $q.defer();
      var optionsString = popup.stringifyOptions(popup.prepareOptions(options));

      popupWindow = $window.open(url, 'Satellizer', optionsString);
      popupWindow.focus();

      this.postMessageHandler(deferred);
      this.pollPopup(deferred);

      return deferred.promise;
    };

    popup.pollPopup = function(deferred) {
      polling = $interval(function() {
        if (popupWindow.closed) {
          $interval.cancel(polling);
          deferred.reject('Popup was closed by the user.');
        }
      }, 35);
    };

    popup.postMessageHandler = function(deferred) {
      $window.addEventListener('message', function(event) {
        if (event.origin === $window.location.origin) {
          deferred.resolve(event.data);
        }
      }, false);
    };

    popup.prepareOptions = function(options) {
      var defaults = {
        width: options.width || 500,
        height: options.height || 500,
        left: $window.screenX + (($window.outerWidth - options.width) / 2),
        top: $window.screenY + (($window.outerHeight - options.height) / 2.5)
      };
      return angular.extend(defaults, options);
    };

    popup.stringifyOptions = function(options) {
      var parts = [];
      angular.forEach(options, function(value, key) {
        parts.push(key + '=' + value);
      });
      return parts.join(',');
    };

    return popup;
  }

  function Oauth1($q, $http, Popup) {
    var defaults = {
      url: null,
      name: null,
      popupOptions: null,
      authorizationEndpoint: null
    };

    var oauth1 = {};

    oauth1.tokenRequest = function() {
      return $http.post(defaults.url);
    };

    oauth1.open = function(options) {
      angular.extend(defaults, options);

      var deferred = $q.defer();

      Popup.open(defaults.url).then(function(oauthData) {
        this.exchangeForToken(oauthData).then(function(response) {
          deferred.resolve(response.data);
        });
      });

      return deferred.promise;
    };

    oauth1.exchangeForToken = function(oauthData) {
      return $http.get(defaults.url, { params: oauthData });
    };

    return oauth1;
  }

  function Oauth2($q, $http, Utils, Popup) {
    var defaults = {
      url: null,
      name: null,
      scope: null,
      clientId: null,
      redirectUri: null,
      popupOptions: null,
      requiredUrlParams: null,
      optionalUrlParams: null,
      authorizationEndpoint: null,
      responseType: 'code',
      defaultUrlParams: ['response_type', 'client_id', 'redirect_uri']
    };

    var oauth2 = {};

    oauth2.open = function(options) {
      angular.extend(defaults, options);
      var deferred = $q.defer();
      var url = oauth2.buildUrl();

      Popup.open(url, defaults.popupOptions).then(function(oauthData) {
        oauth2.exchangeForToken(oauthData).then(function(response) {
          deferred.resolve(response.data);
        });
      });

      return deferred.promise;
    };

    oauth2.exchangeForToken = function(oauthData) {
      return $http.post(defaults.url, {
        code: oauthData.code,
        clientId: defaults.clientId,
        redirectUri: defaults.redirectUri
      });
    };

    oauth2.buildUrl = function() {
      var baseUrl = defaults.authorizationEndpoint;
      var qs = oauth2.buildQueryString();
      return [baseUrl, qs].join('?');
    };

    oauth2.buildQueryString = function() {
      var keyValuePairs = [];
      var urlParams = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams'];

      angular.forEach(urlParams, function(params) {
        angular.forEach(defaults[params], function(paramName) {
          var camelizedName = Utils.camelCase(paramName);
          var paramValue = defaults[camelizedName];
          keyValuePairs.push([paramName, encodeURIComponent(paramValue)]);
        });
      });

      return keyValuePairs.map(function(pair) {
        return pair.join('=');
      }).join('&');
    };

    return oauth2;
  }

  function httpInterceptor($httpProvider) {
    $httpProvider.interceptors.push(function($q, $window, $location) {
      return {
        request: function(config) {
          if ($window.localStorage.jwtToken) {
            config.headers.Authorization = 'Bearer ' + $window.localStorage.jwtToken;
          }
          return config;
        },
        responseError: function(response) {
          if (response.status === 401 || response.status === 403) {
            $location.path('/login');
          }
          return $q.reject(response);
        }
      };
    });
  }

  function onRun($rootScope, $window, $location, Utils) {
    var token = $window.localStorage.jwtToken;
    if (token) {
      var payload = JSON.parse($window.atob(token.split('.')[1]));
      $rootScope.currentUser = payload.user;
    }

    var params = $window.location.search.substring(1);
    var qs = Utils.parseQueryString(params);
    if ($window.opener && $window.opener.location.origin === $window.location.origin) {
      if (qs.oauth_token && qs.oauth_verifier) {
        $window.opener.postMessage({ oauth_token: qs.oauth_token, oauth_verifier: qs.oauth_verifier }, '*');
      } else if (qs.code) {
        $window.opener.postMessage({ code: qs.code }, '*');
      }
      $window.close();
    }

    $rootScope.$on('$routeChangeStart', function(event, current) {
      if ($rootScope[config.user] &&
        (current.originalPath === '/login' || current.originalPath === '/signup')) {
        $location.path('/');
      }
      if (current.authenticated && !$rootScope[config.user]) {
        $location.path('/login');
      }
    });
  }

  function Utils() {
    this.camelCase = function(name) {
      return name.replace(/([\:\-\_]+(.))/g, function(_, separator, letter, offset) {
        return offset ? letter.toUpperCase() : letter;
      });
    };
    this.parseQueryString = function(keyValue) {
      var obj = { }, key, value;
      angular.forEach((keyValue || '').split('&'), function(keyValue) {
        if (keyValue) {
          value = keyValue.split('=');
          key = decodeURIComponent(value[0]);
          obj[key] = angular.isDefined(value[1]) ? decodeURIComponent(value[1]) : true;
        }
      });
      return obj;
    };
  }

})(window, window.angular);

<|endoftext|>


<commit_before>/**
 * Satellizer 0.0.1
 * (c) 2014 Sahat Yalkabov <sahat@me.com>
 * License: MIT
 */

(function(window, angular, undefined) {
  'use strict';

  angular.module('Satellizer', [])
    .service('Utils', Utils)
    .factory('Local', Local)
    .factory('Oauth2', Oauth2)
    .factory('Oauth1', Oauth1)
    .factory('Popup', Popup)
    .provider('$auth', $auth)
    .config(httpInterceptor)
    .run(onRun);

  var config = {
    logoutRedirect: '/',
    loginRedirect: '/',
    loginUrl: '/auth/login',
    signupUrl: '/auth/signup',
    signupRedirect: '/login',
    loginRoute: '/login',
    signupRoute: '/signup',
    user: 'currentUser'
  };

  var providers = {
    google: {
      url: '/auth/google',
      authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth',
      redirectUri: window.location.origin,
      scope: 'openid profile email',
      requiredUrlParams: ['scope'],
      optionalUrlParams: ['display'],
      display: 'popup',
      type: 'oauth2',
      popupOptions: {
        width: 452,
        height: 633
      }
    },
    facebook: {
      url: '/auth/facebook',
      authorizationEndpoint: 'https://www.facebook.com/dialog/oauth',
      redirectUri: window.location.origin + '/',
      scope: 'email',
      requiredUrlParams: ['display'],
      display: 'popup',
      type: 'oauth2',
      popupOptions: {
        width: 481,
        height: 269
      }
    },
    linkedin: {
      url: '/auth/linkedin',
      authorizationEndpoint: 'https://www.linkedin.com/uas/oauth2/authorization',
      redirectUri: window.location.origin,
      requiredUrlParams: ['state'],
      state: 'STATE',
      type: 'oauth2',
      popupOptions: {
        width: 527,
        height: 582
      }
    },
    twitter: {
      url: '/auth/twitter',
      authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate',
      type: 'oauth1'
    }
  };

  function $auth() {

    this.config = config;

    this.setProvider = function(params) {
      angular.extend(providers[params.name], params);
    };

    this.addProvider = function(params) {
      providers[params.name] = {};
      angular.extend(providers[params.name], params);
    };

    this.getProvider = function(name) {
      return providers[name];
    };

    this.$get = function(Oauth1, Oauth2, Local, $http, $q) {

      var $auth = {};

      $auth.authenticate = function(providerName) {
        var deferred = $q.defer();
        var provider = (providers[providerName].type === 'oauth1') ? Oauth1 : Oauth2;
        provider.open(providers[providerName]).then(function(token) {
          Local.parseUser(token, deferred);
        });
        return deferred.promise;
      };

      $auth.login = function(user) {
        Local.login(user);
      };

      $auth.signup = function(user) {
        Local.signup(user);
      };

      $auth.logout = function() {
        Local.logout();
      };

      $auth.isAuthenticated = function() {
        Local.isAuthenticated();
      };

      return $auth;
    };

  }

  function Local($q, $http, $window, $rootScope, $location) {

    var local = {};

    local.parseUser = function(token, deferred) {
      var payload = JSON.parse($window.atob(token.split('.')[1]));
      $window.localStorage.jwtToken = token;
      $rootScope[config.user] = payload.user;
      $location.path(config.loginRedirect);
      deferred.resolve(payload.user);
    };

    local.login = function(user) {
      var deferred = $q.defer();
      $http.post(config.loginUrl, user)
        .success(function(token) {
          local.parseUser(token, deferred);
        })
        .error(function(error) {
          deferred.reject(error);
        });
      return deferred.promise;
    };

    local.signup = function(user) {
      $http.post(config.signupUrl, user).then(function() {
        $location.path(config.signupRedirect);
      });
    };

    local.logout = function() {
      delete $rootScope[config.user];
      delete $window.localStorage.jwtToken;
      $location.path(config.logoutRedirect);
    };

    local.isAuthenticated = function() {
      return Boolean($rootScope.currentUser);
    };

    return local;
  }

  function Popup($q, $interval, $window) {
    var popupWindow = null;
    var polling = null;

    var popup = {};

    popup.popupWindow = popupWindow;

    popup.open = function(url, options) {
      var deferred = $q.defer();
      var optionsString = popup.stringifyOptions(popup.prepareOptions(options));

      popupWindow = $window.open(url, 'Satellizer', optionsString);
      popupWindow.focus();

      popup.postMessageHandler(deferred);
      popup.pollPopup(deferred);

      return deferred.promise;
    };

    popup.pollPopup = function(deferred) {
      polling = $interval(function() {
        if (popupWindow.closed) {
          $interval.cancel(polling);
          deferred.reject('Popup was closed by the user');
        }
      }, 35);
    };

    popup.postMessageHandler = function(deferred) {
      $window.addEventListener('message', function(event) {
        if (event.origin === $window.location.origin) {
          deferred.resolve(event.data);
        }
      }, false);
    };

    popup.prepareOptions = function(options) {
      options = options || {};
      var width = options.width || 500;
      var height = options.height || 500;
      var defaults = {
        width: width,
        height: height,
        left: $window.screenX + (($window.outerWidth - width) / 2),
        top: $window.screenY + (($window.outerHeight - height) / 2.5)
      };
      return angular.extend(defaults, options);
    };

    popup.stringifyOptions = function(options) {
      var parts = [];
      angular.forEach(options, function(value, key) {
        parts.push(key + '=' + value);
      });
      return parts.join(',');
    };

    return popup;
  }

  function Oauth1($q, $http, Popup) {
    var defaults = {
      url: null,
      name: null,
      popupOptions: null,
      authorizationEndpoint: null
    };

    var oauth1 = {};

    oauth1.open = function(options) {
      angular.extend(defaults, options);

      var deferred = $q.defer();

      Popup.open(defaults.url).then(function(oauthData) {
        oauth1.exchangeForToken(oauthData).then(function(response) {
          deferred.resolve(response.data);
        });
      });

      return deferred.promise;
    };

    oauth1.exchangeForToken = function(oauthData) {
      console.log(oauthData)
      return $http.get(defaults.url + '?' + encodeURIComponent(oauthData));
    };

    return oauth1;
  }

  function Oauth2($q, $http, Utils, Popup) {
    var defaults = {
      url: null,
      name: null,
      scope: null,
      clientId: null,
      redirectUri: null,
      popupOptions: null,
      requiredUrlParams: null,
      optionalUrlParams: null,
      authorizationEndpoint: null,
      responseType: 'code',
      defaultUrlParams: ['response_type', 'client_id', 'redirect_uri']
    };

    var oauth2 = {};

    oauth2.open = function(options) {
      angular.extend(defaults, options);
      var deferred = $q.defer();
      var url = oauth2.buildUrl();

      Popup.open(url, defaults.popupOptions).then(function(oauthData) {
        oauth2.exchangeForToken(oauthData).then(function(response) {
          deferred.resolve(response.data);
        });
      });

      return deferred.promise;
    };

    oauth2.exchangeForToken = function(oauthData) {
      return $http.post(defaults.url, {
        code: oauthData.code,
        clientId: defaults.clientId,
        redirectUri: defaults.redirectUri
      });
    };

    oauth2.buildUrl = function() {
      var baseUrl = defaults.authorizationEndpoint;
      var qs = oauth2.buildQueryString();
      return [baseUrl, qs].join('?');
    };

    oauth2.buildQueryString = function() {
      var keyValuePairs = [];
      var urlParams = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams'];

      angular.forEach(urlParams, function(params) {
        angular.forEach(defaults[params], function(paramName) {
          var camelizedName = Utils.camelCase(paramName);
          var paramValue = defaults[camelizedName];
          keyValuePairs.push([paramName, encodeURIComponent(paramValue)]);
        });
      });

      return keyValuePairs.map(function(pair) {
        return pair.join('=');
      }).join('&');
    };

    return oauth2;
  }

  function httpInterceptor($httpProvider) {
    $httpProvider.interceptors.push(function($q, $window, $location) {
      return {
        request: function(config) {
          if ($window.localStorage.jwtToken) {
            config.headers.Authorization = 'Bearer ' + $window.localStorage.jwtToken;
          }
          return config;
        },
        responseError: function(response) {
          if (response.status === 401 || response.status === 403) {
            delete $window.localStorage.jwtToken;
            $location.path('/login');
          }
          return $q.reject(response);
        }
      };
    });
  }

  function onRun($rootScope, $window, $location, Utils) {
    var token = $window.localStorage.jwtToken;
    if (token) {
      var payload = JSON.parse($window.atob(token.split('.')[1]));
      $rootScope.currentUser = payload.user;
    }

    var params = $window.location.search.substring(1);
    var qs = Utils.parseQueryString(params);
    if ($window.opener && $window.opener.location.origin === $window.location.origin) {
      if (qs.oauth_token && qs.oauth_verifier) {
        $window.opener.postMessage({ oauth_token: qs.oauth_token, oauth_verifier: qs.oauth_verifier }, '*');
      } else if (qs.code) {
        $window.opener.postMessage({ code: qs.code }, '*');
      }
      $window.close();
    }

    $rootScope.$on('$routeChangeStart', function(event, current) {
      if ($rootScope[config.user] &&
        (current.originalPath === '/login' || current.originalPath === '/signup')) {
        $location.path('/');
      }
      if (current.authenticated && !$rootScope[config.user]) {
        $location.path('/login');
      }
    });
  }

  function Utils() {
    this.camelCase = function(name) {
      return name.replace(/([\:\-\_]+(.))/g, function(_, separator, letter, offset) {
        return offset ? letter.toUpperCase() : letter;
      });
    };
    this.parseQueryString = function(keyValue) {
      var obj = { }, key, value;
      angular.forEach((keyValue || '').split('&'), function(keyValue) {
        if (keyValue) {
          value = keyValue.split('=');
          key = decodeURIComponent(value[0]);
          obj[key] = angular.isDefined(value[1]) ? decodeURIComponent(value[1]) : true;
        }
      });
      return obj;
    };
    this.buildQueryString = function(obj) {
      var str = [];
      for (var p in obj) {
        var k = p,
          v = obj[k];
        str.push(angular.isObject(v) ? this.buildQueryString(v, k) : (k) + "=" + encodeURIComponent(v));
      }
      return str.join('&');
    }
  }

})(window, window.angular);

<commit_msg>exchangeForToken uses custom query string builder function<commit_after>/**
 * Satellizer 0.0.1
 * (c) 2014 Sahat Yalkabov <sahat@me.com>
 * License: MIT
 */

(function(window, angular, undefined) {
  'use strict';

  angular.module('Satellizer', [])
    .service('Utils', Utils)
    .factory('Local', Local)
    .factory('Oauth2', Oauth2)
    .factory('Oauth1', Oauth1)
    .factory('Popup', Popup)
    .provider('$auth', $auth)
    .config(httpInterceptor)
    .run(onRun);

  var config = {
    logoutRedirect: '/',
    loginRedirect: '/',
    loginUrl: '/auth/login',
    signupUrl: '/auth/signup',
    signupRedirect: '/login',
    loginRoute: '/login',
    signupRoute: '/signup',
    user: 'currentUser'
  };

  var providers = {
    google: {
      url: '/auth/google',
      authorizationEndpoint: 'https://accounts.google.com/o/oauth2/auth',
      redirectUri: window.location.origin,
      scope: 'openid profile email',
      requiredUrlParams: ['scope'],
      optionalUrlParams: ['display'],
      display: 'popup',
      type: 'oauth2',
      popupOptions: {
        width: 452,
        height: 633
      }
    },
    facebook: {
      url: '/auth/facebook',
      authorizationEndpoint: 'https://www.facebook.com/dialog/oauth',
      redirectUri: window.location.origin + '/',
      scope: 'email',
      requiredUrlParams: ['display'],
      display: 'popup',
      type: 'oauth2',
      popupOptions: {
        width: 481,
        height: 269
      }
    },
    linkedin: {
      url: '/auth/linkedin',
      authorizationEndpoint: 'https://www.linkedin.com/uas/oauth2/authorization',
      redirectUri: window.location.origin,
      requiredUrlParams: ['state'],
      state: 'STATE',
      type: 'oauth2',
      popupOptions: {
        width: 527,
        height: 582
      }
    },
    twitter: {
      url: '/auth/twitter',
      authorizationEndpoint: 'https://api.twitter.com/oauth/authenticate',
      type: 'oauth1'
    }
  };

  function $auth() {

    this.config = config;

    this.setProvider = function(params) {
      angular.extend(providers[params.name], params);
    };

    this.addProvider = function(params) {
      providers[params.name] = {};
      angular.extend(providers[params.name], params);
    };

    this.getProvider = function(name) {
      return providers[name];
    };

    this.$get = function(Oauth1, Oauth2, Local, $http, $q) {

      var $auth = {};

      $auth.authenticate = function(providerName) {
        var deferred = $q.defer();
        var provider = (providers[providerName].type === 'oauth1') ? Oauth1 : Oauth2;
        provider.open(providers[providerName]).then(function(token) {
          Local.parseUser(token, deferred);
        });
        return deferred.promise;
      };

      $auth.login = function(user) {
        Local.login(user);
      };

      $auth.signup = function(user) {
        Local.signup(user);
      };

      $auth.logout = function() {
        Local.logout();
      };

      $auth.isAuthenticated = function() {
        Local.isAuthenticated();
      };

      return $auth;
    };

  }

  function Local($q, $http, $window, $rootScope, $location) {

    var local = {};

    local.parseUser = function(token, deferred) {
      var payload = JSON.parse($window.atob(token.split('.')[1]));
      $window.localStorage.jwtToken = token;
      $rootScope[config.user] = payload.user;
      $location.path(config.loginRedirect);
      deferred.resolve(payload.user);
    };

    local.login = function(user) {
      var deferred = $q.defer();
      $http.post(config.loginUrl, user)
        .success(function(token) {
          local.parseUser(token, deferred);
        })
        .error(function(error) {
          deferred.reject(error);
        });
      return deferred.promise;
    };

    local.signup = function(user) {
      $http.post(config.signupUrl, user).then(function() {
        $location.path(config.signupRedirect);
      });
    };

    local.logout = function() {
      delete $rootScope[config.user];
      delete $window.localStorage.jwtToken;
      $location.path(config.logoutRedirect);
    };

    local.isAuthenticated = function() {
      return Boolean($rootScope.currentUser);
    };

    return local;
  }

  function Popup($q, $interval, $window) {
    var popupWindow = null;
    var polling = null;

    var popup = {};

    popup.popupWindow = popupWindow;

    popup.open = function(url, options) {
      var deferred = $q.defer();
      var optionsString = popup.stringifyOptions(popup.prepareOptions(options));

      popupWindow = $window.open(url, 'Satellizer', optionsString);
      popupWindow.focus();

      popup.postMessageHandler(deferred);
      popup.pollPopup(deferred);

      return deferred.promise;
    };

    popup.pollPopup = function(deferred) {
      polling = $interval(function() {
        if (popupWindow.closed) {
          $interval.cancel(polling);
          deferred.reject('Popup was closed by the user');
        }
      }, 35);
    };

    popup.postMessageHandler = function(deferred) {
      $window.addEventListener('message', function(event) {
        if (event.origin === $window.location.origin) {
          deferred.resolve(event.data);
        }
      }, false);
    };

    popup.prepareOptions = function(options) {
      options = options || {};
      var width = options.width || 500;
      var height = options.height || 500;
      var defaults = {
        width: width,
        height: height,
        left: $window.screenX + (($window.outerWidth - width) / 2),
        top: $window.screenY + (($window.outerHeight - height) / 2.5)
      };
      return angular.extend(defaults, options);
    };

    popup.stringifyOptions = function(options) {
      var parts = [];
      angular.forEach(options, function(value, key) {
        parts.push(key + '=' + value);
      });
      return parts.join(',');
    };

    return popup;
  }

  function Oauth1($q, $http, Popup, Utils) {
    var defaults = {
      url: null,
      name: null,
      popupOptions: null,
      authorizationEndpoint: null
    };

    var oauth1 = {};

    oauth1.open = function(options) {
      angular.extend(defaults, options);

      var deferred = $q.defer();

      Popup.open(defaults.url).then(function(oauthData) {
        oauth1.exchangeForToken(oauthData).then(function(response) {
          deferred.resolve(response.data);
        });
      });

      return deferred.promise;
    };

    oauth1.exchangeForToken = function(oauthData) {
      oauthData = Utils.buildQueryString(oauthData);
      return $http.get(defaults.url + '?' + oauthData);
    };

    return oauth1;
  }

  function Oauth2($q, $http, Utils, Popup) {
    var defaults = {
      url: null,
      name: null,
      scope: null,
      clientId: null,
      redirectUri: null,
      popupOptions: null,
      requiredUrlParams: null,
      optionalUrlParams: null,
      authorizationEndpoint: null,
      responseType: 'code',
      defaultUrlParams: ['response_type', 'client_id', 'redirect_uri']
    };

    var oauth2 = {};

    oauth2.open = function(options) {
      angular.extend(defaults, options);
      var deferred = $q.defer();
      var url = oauth2.buildUrl();

      Popup.open(url, defaults.popupOptions).then(function(oauthData) {
        oauth2.exchangeForToken(oauthData).then(function(response) {
          deferred.resolve(response.data);
        });
      });

      return deferred.promise;
    };

    oauth2.exchangeForToken = function(oauthData) {
      return $http.post(defaults.url, {
        code: oauthData.code,
        clientId: defaults.clientId,
        redirectUri: defaults.redirectUri
      });
    };

    oauth2.buildUrl = function() {
      var baseUrl = defaults.authorizationEndpoint;
      var qs = oauth2.buildQueryString();
      return [baseUrl, qs].join('?');
    };

    oauth2.buildQueryString = function() {
      var keyValuePairs = [];
      var urlParams = ['defaultUrlParams', 'requiredUrlParams', 'optionalUrlParams'];

      angular.forEach(urlParams, function(params) {
        angular.forEach(defaults[params], function(paramName) {
          var camelizedName = Utils.camelCase(paramName);
          var paramValue = defaults[camelizedName];
          keyValuePairs.push([paramName, encodeURIComponent(paramValue)]);
        });
      });

      return keyValuePairs.map(function(pair) {
        return pair.join('=');
      }).join('&');
    };

    return oauth2;
  }

  function httpInterceptor($httpProvider) {
    $httpProvider.interceptors.push(function($q, $window, $location) {
      return {
        request: function(config) {
          if ($window.localStorage.jwtToken) {
            config.headers.Authorization = 'Bearer ' + $window.localStorage.jwtToken;
          }
          return config;
        },
        responseError: function(response) {
          if (response.status === 401 || response.status === 403) {
            delete $window.localStorage.jwtToken;
            $location.path('/login');
          }
          return $q.reject(response);
        }
      };
    });
  }

  function onRun($rootScope, $window, $location, Utils) {
    var token = $window.localStorage.jwtToken;
    if (token) {
      var payload = JSON.parse($window.atob(token.split('.')[1]));
      $rootScope.currentUser = payload.user;
    }

    var params = $window.location.search.substring(1);
    var qs = Utils.parseQueryString(params);
    if ($window.opener && $window.opener.location.origin === $window.location.origin) {
      if (qs.oauth_token && qs.oauth_verifier) {
        $window.opener.postMessage({ oauth_token: qs.oauth_token, oauth_verifier: qs.oauth_verifier }, '*');
      } else if (qs.code) {
        $window.opener.postMessage({ code: qs.code }, '*');
      }
      $window.close();
    }

    $rootScope.$on('$routeChangeStart', function(event, current) {
      if ($rootScope[config.user] &&
        (current.originalPath === '/login' || current.originalPath === '/signup')) {
        $location.path('/');
      }
      if (current.authenticated && !$rootScope[config.user]) {
        $location.path('/login');
      }
    });
  }

  function Utils() {
    this.camelCase = function(name) {
      return name.replace(/([\:\-\_]+(.))/g, function(_, separator, letter, offset) {
        return offset ? letter.toUpperCase() : letter;
      });
    };
    this.parseQueryString = function(keyValue) {
      var obj = { }, key, value;
      angular.forEach((keyValue || '').split('&'), function(keyValue) {
        if (keyValue) {
          value = keyValue.split('=');
          key = decodeURIComponent(value[0]);
          obj[key] = angular.isDefined(value[1]) ? decodeURIComponent(value[1]) : true;
        }
      });
      return obj;
    };
    this.buildQueryString = function(obj) {
      var str = [];
      for (var p in obj) {
        var k = p,
          v = obj[k];
        str.push(angular.isObject(v) ? this.buildQueryString(v, k) : (k) + "=" + encodeURIComponent(v));
      }
      return str.join('&');
    }
  }

})(window, window.angular);

<|endoftext|>


<commit_before>var libxmljs = require("libxmljs")
var parseUrl = require('url').parse
var redis = require("redis")
var logger = require('./logger')('cas_validate::sax_parser')
var set_session = require('./ticket_store').set_session
var _ = require('underscore')

function make_sax_parser(req,res,next){
    var saxparser = new libxmljs.SaxParser()
    var errors
    var usercharhandler=function(chars){
    if(req.session.name){
                req.session.name += chars
            }else{
                req.session.name = chars
            }
            return null
        }
        req.session.attributes={}
        var attrcharhandler=function(attrname){
            return function(chars){
                req.session.attributes[attrname]=chars
            }
        }
        var attrhandler=function(e,a,p,u,n){
            saxparser.once('characters',attrcharhandler(e))
        }

        function startHandler(elem, attrs, prefix, uri, namespaces) {
            if(elem === 'authenticationSuccess'){
                logger.debug('auth passed ');
                req.session.st = req.session.ticket;
                // stuff into a redis session for single sign off
                // set_session(req.session.st,req.sessionID);
                set_session(req,function(err){
                    if(err){
                        errors = err
                        return next(err)
                    }
                    return null
                })
            }
            if(elem==='authenticationFailure'){
                var attr = attrs[0]
                if(attr[0]==='code' && attr[3]==='INVALID_TICKET'){
                    logger.debug('auth failed') ;
                    delete req.session.st
                }
            }
            if(elem==='user'){
                // valid user
                saxparser.on('characters',usercharhandler)
            }
            if(elem==='attributes'){
                // valid user
                saxparser.on('startElementNS',attrhandler)
            }
            return null
        }
        saxparser.on('startElementNS',startHandler)

        function endHandler(elem, attrs, prefix, uri, namespaces) {

            // could also use once above, but this is safer
            if(elem==='user'){
                // turn off user chars handler
                saxparser.removeListener('characters',usercharhandler)
            }
            if(elem==='attributes'){
                // valid attributes, done
                saxparser.removeListener('startElementNS',attrhandler)
            }
        }
        saxparser.on('endElementNS',endHandler)
        saxparser.on('endDocument',function(){
            // strip the ticket and redirect back
            // // this doesn't work properly
            // if(url.query.ticket){
            //     delete url.query.ticket
            //     delete url.search
            // }
            //
            // the above is okay, but fails because it does nothing.
            // redirecting works, but kills the session, so you end up
            // in a redirect loop.
            //
            // no matter what I try, redirect will kill the session.
            // so just live with the ticket for now until I figure this out
            if(req.session.st){
                logger.debug('parsed xml doc, got:  user name = '+req.session.name+', attributes are: ' +JSON.stringify(req.session.attributes))
                if(!errors){
                    next()
                }
            }else{
                // possibly a bad, stale ticket in request
                var url = parseUrl(req.url,true);
                var path = url.pathname
                res.writeHead(307, { 'location': path })
                res.end()
            }
            return null
        })
    return saxparser
}

/**
 * get_username
 *
 * get the user name provided by the CAS server, stored in the current session
 *
 * You can also directly access them at req.session.name, but
 * just in case that changes you can always rely on this to do the
 * right thing
 *
 * will invoke the callback with (error,{'username':username})
 *
 * If there is no username (perhaps you do not have a CAS session),
 * then it will invoke callback with (error,{}) (an empty object)
 *
 * @param {Object} req the http.IncomingMessage (from express or connect)
 * @param {Function} next the callback to invoke with the username object
 * @return {Object} returns whatever next invocation returns
 * @api public
 *
 */
function get_username(req,next){
    if(req.session !== undefined && req.session.name !== undefined){
        return next(null,{'user_name':req.session.name})
    }else{
        return next(null,{})
    }
}

/**
 * get_attributes
 *
 * get the user attributes parsed by xml parser and stored in the
 * session. Also, get the username too while we're at it.
 *
 * You can also directly access them at req.session.attributes, but
 * just in case that changes you can always rely on this to do the
 * right thing
 *
 * will invoke the callback with (error,attributes) attributes will
 * include username as well.  see the documentation above for
 * get_username
 *
 * if there are not attributes, will return only the username object
 *
 * If the attributes are empty and there is no username (perhaps there
 * is not a CAS session for this user), will invoke callback with
 * (error,{}) (an empty object)
 *
 * @param {Object} req the http.IncomingMessage (from express or connect)
 * @param {Function} next the callback to invoke with the attributes
 * @return {Object} returns whatever next invocation returns
 * @api public
 *
 */
function get_attributes(req,next){
    return get_username(req,function(err,obj){
               if(req.session !== undefined && req.session.attributes !== undefined){
                   return next(null,_.extend(obj,req.session.attributes))
               }else{
                   return next(null,obj)
               }
           })
}

exports.make_sax_parser=make_sax_parser
exports.get_username=get_username
exports.get_attributes=get_attributes
<commit_msg>clean up some sax parsing<commit_after>var libxmljs = require("libxmljs")
var parseUrl = require('url').parse
var redis = require("redis")
var logger = require('./logger')('cas_validate::sax_parser')
var set_session = require('./ticket_store').set_session
var _ = require('underscore')

function make_sax_parser(req,res,next){
    var saxparser = new libxmljs.SaxParser()
    var errors
    req.session.attributes={}
    delete req.session.name

    function usercharhandler(chars){
        req.session.name = chars
        return null
    }
    function attrcharhandler(attrname){
        return function(chars){
            req.session.attributes[attrname]=chars
            return null
        }
    }
    function attrhandler(e,a,p,u,n){
        saxparser.once('characters',attrcharhandler(e))
    }

    function startHandler(elem, attrs, prefix, uri, namespaces) {
        if(elem === 'authenticationSuccess'){
            logger.debug('auth passed ');
            req.session.st = req.session.ticket;
            // stuff into a redis session for single sign off
            // set_session(req.session.st,req.sessionID);
            set_session(req,function(err){
                if(err){
                    errors = err
                    return next(err)
                }
                return null
            })
        }
        if(elem==='authenticationFailure'){
            var attr = attrs[0]
            if(attr[0]==='code' && attr[3]==='INVALID_TICKET'){
                logger.debug('auth failed') ;
                delete req.session.st
            }
        }
        if(elem==='user'){
            // valid user
            saxparser.on('characters',usercharhandler)
        }
        if(elem==='attributes'){
            // valid user
            saxparser.on('startElementNS',attrhandler)
        }
        return null
    }
    saxparser.on('startElementNS',startHandler)

    function endHandler(elem, attrs, prefix, uri, namespaces) {

        // could also use once above, but this is safer
        if(elem==='user'){
            // turn off user chars handler
            saxparser.removeListener('characters',usercharhandler)
        }
        if(elem==='attributes'){
            // valid attributes, done
            saxparser.removeListener('startElementNS',attrhandler)
        }
    }
    saxparser.on('endElementNS',endHandler)
    saxparser.on('endDocument',function(){
        // strip the ticket and redirect back
        // // this doesn't work properly
        // if(url.query.ticket){
        //     delete url.query.ticket
        //     delete url.search
        // }
        //
        // the above is okay, but fails because it does nothing.
        // redirecting works, but kills the session, so you end up
        // in a redirect loop.
        //
        // no matter what I try, redirect will kill the session.
        // so just live with the ticket for now until I figure this out
        if(req.session.st){
            logger.debug('parsed xml doc, got:  user name = '+req.session.name+', attributes are: ' +JSON.stringify(req.session.attributes))
            if(!errors){
                next()
            }
        }else{
            // possibly a bad, stale ticket in request
            var url = parseUrl(req.url,true);
            var path = url.pathname
            res.writeHead(307, { 'location': path })
            res.end()
        }
        return null
    })
    return saxparser
}

/**
 * get_username
 *
 * get the user name provided by the CAS server, stored in the current session
 *
 * You can also directly access them at req.session.name, but
 * just in case that changes you can always rely on this to do the
 * right thing
 *
 * will invoke the callback with (error,{'username':username})
 *
 * If there is no username (perhaps you do not have a CAS session),
 * then it will invoke callback with (error,{}) (an empty object)
 *
 * @param {Object} req the http.IncomingMessage (from express or connect)
 * @param {Function} next the callback to invoke with the username object
 * @return {Object} returns whatever next invocation returns
 * @api public
 *
 */
function get_username(req,next){
    if(req.session !== undefined && req.session.name !== undefined){
        return next(null,{'user_name':req.session.name})
    }else{
        return next(null,{})
    }
}

/**
 * get_attributes
 *
 * get the user attributes parsed by xml parser and stored in the
 * session. Also, get the username too while we're at it.
 *
 * You can also directly access them at req.session.attributes, but
 * just in case that changes you can always rely on this to do the
 * right thing
 *
 * will invoke the callback with (error,attributes) attributes will
 * include username as well.  see the documentation above for
 * get_username
 *
 * if there are not attributes, will return only the username object
 *
 * If the attributes are empty and there is no username (perhaps there
 * is not a CAS session for this user), will invoke callback with
 * (error,{}) (an empty object)
 *
 * @param {Object} req the http.IncomingMessage (from express or connect)
 * @param {Function} next the callback to invoke with the attributes
 * @return {Object} returns whatever next invocation returns
 * @api public
 *
 */
function get_attributes(req,next){
    return get_username(req,function(err,obj){
               if(req.session !== undefined && req.session.attributes !== undefined){
                   return next(null,_.extend(obj,req.session.attributes))
               }else{
                   return next(null,obj)
               }
           })
}

exports.make_sax_parser=make_sax_parser
exports.get_username=get_username
exports.get_attributes=get_attributes
<|endoftext|>


<commit_before>'use strict';

var asynEach = require('async-foreach').forEach,
    chalk = require('chalk'),
    path = require('path'),
    phantom = require('phantom'),
    Q = require('q'),
    utils = require('./utils');

var screenshotExclusions = ['pdf'];

/**
 * Loops through defined viewports, rendering passed url, and taking screenshots using PhantomJS
 * @author Steven Britton
 * @date   2016-04-14
 * @param  {[type]}   options [description]
 * @param  {String}   url     [description]
 * @param  {Function} done    [description]
 * @return {[type]}           [description]
 */
function renderPage(options, url, done) {

    utils.log.verbose('Screenshot: renderPage');

    var folder = utils.urlToDir(url),
        phantomOptions=[],
        phantomOutObj,
        phantomOutObj2,
        viewports = options.viewports,
        output, key;

    function handleMsgStack(msgStack){

        if(Array.isArray(msgStack) && msgStack.length > 0){
            console.log('\n    ', chalk.red.bold(msgStack.join('\n     ')), '\n');
        }
    }

    if(options.sitecrawler_options.ignoreInvalidSSL){
        phantomOptions.push('--ignore-ssl-errors=yes');
    }

    phantom.create(phantomOptions).then(function(ph) {

        ph.createPage().then(function(page) {

            // do we have stored credentials?
            if (options.domain.auth.user !== null && options.domain.auth.pwd !== null) {

                utils.log.verbose('\n   ⤷  Setting Authentication');

                page.setting('userName', options.domain.auth.user);
                page.setting('password', options.domain.auth.pwd);
            }

            // Create an Output object to pass through Phantom since PhantomJS does not share any memory or variables with node
            phantomOutObj = ph.createOutObject();
            phantomOutObj2 = ph.createOutObject();

            // Create property to tracking messages while working with PhantomJS
            phantomOutObj.msgStackJS = [];
            phantomOutObj2.msgStackResources = [];

            /**
             * Callback function that's invoked when there is a JavaScript execution error
             * @author Steven Britton
             * @date   2016-05-12
             * @param  {String}   msg           error messages
             * @param  {Object}   trace         error trace
             * @param  {Array}    out           captures and returns error messages
             * @param  {Object}   phantomOutObj
             * @return {Object}   phantomOutObj
             */
            page.property('onError', function(msg, trace, out) {

                out.msgStackJS.push('');
                out.msgStackJS.push('JS ERROR:');
                out.msgStackJS.push('');
                out.msgStackJS.push(msg);

                if (trace && trace.length) {
                    trace.forEach(function(t) {
                        out.msgStackJS.push('File: ' + t.file);
                        out.msgStackJS.push('Line: ' + t.line);
                        out.msgStackJS.push('Function: ' + t.function);
                    });
                }
            }, phantomOutObj);

            /**
             * Callback function that's invoked when there is a resource load error
             * @author Steven Britton
             * @date   2016-05-12
             * @param  {String}   msg           error messages
             * @param  {Object}   trace         error trace
             * @param  {Array}    out           captures and returns error messages
             * @param  {Object}   phantomOutObj
             * @return {Object}   phantomOutObj
             */

            page.property('onResourceError', function(resourceError, out) {

                out.msgStackResources.push('');
                out.msgStackResources.push('ResourceError: ' + resourceError.url);
                out.msgStackResources.push('Error code: ' + resourceError.errorCode);
                out.msgStackResources.push('Error Description: ' + resourceError.errorString);

            }, phantomOutObj2);

            /**
             * PhantonJS Page Open function
             * @author Steven Britton
             */
            page.open(url).then(function(status) {

                function renderView(n) {

                    if (!!n) {
                        key = n - 1;

                        //var _pageContent = page.property('content');

                        console.log(chalk.green.bold('    ✔  Viewport:'), chalk.green(viewports[key].width + 'x' + viewports[key].height));

                        page.property('viewportSize', { 'width': viewports[key].width, 'height': viewports[key].height });

                        output = path.join(options.paths.output, folder, viewports[key].width + '.png');

                        setTimeout(function() {
                            page.render(output).then(function() {

                                /*
                                pageContent.push({
                                    'page': key,
                                    'content': _pageContent
                                });*/

                                renderView(key);

                            });
                        }, options.screenshot.delay);

                    } else {

                        ph.exit(0);

                        done();
                    }
                }

                if (status === 'success') {

                    console.log('\n   ', chalk.underline.yellow(folder));

                    renderView(viewports.length);

                } else {
                    ph.exit(1);

                    return done('Failed connection: ' + url);
                }
            });

            /**
             * Callback functions for custom output properties
             * @author Steven Britton
             */
            phantomOutObj.property('msgStackJS').then(function(msgStack) {

                handleMsgStack(msgStack);
            });

             phantomOutObj2.property('msgStackResources').then(function(msgStack) {
                handleMsgStack(msgStack);
            });

        });
    });
}

/**
 * Screenshot Object
 * @author Steven Britton
 * @date   2016-04-14
 * @param  {Array}   pages   list of links passed from sitemap.xml file
 * @param  {Object}  options [description]
 */
function Screenshot(parameters) {

    utils.log.verbose('Screenshot: constructor', parameters);

    console.log(chalk.yellow.bold(' ⤷ Generating screenshots'));

    this.pages = parameters.pages;
    this.options = parameters.options;

    var exclusions, regExclusions;

    // format exclusions
    exclusions = screenshotExclusions.join('|');

    // regular expression for exclusions
    regExclusions = new RegExp('\.(' + exclusions + ')', 'i');

    // filter screenshotExclusions
    this.pages = this.pages.filter(function(page) {
        return !page.match(regExclusions);
    });

}

Screenshot.prototype.start = function() {

    utils.log.verbose('Screenshot: start');

    var d = Q.defer(),
        options = this.options;

    asynEach(this.pages, function(page) {

        utils.log.verbose('Screenshot: start asynEach', page);

        var done = this.async();

        renderPage(options, page, function(err) {

            if (err) {
                utils.log.error(err);
            }

            done();
        });

    }, function(success, array) {

        if (success) {
            d.resolve(array);

        } else {
            d.reject(success);
        }

    });

    return d.promise;
};

module.exports = Screenshot;
<commit_msg>feat(screenshots):  Inject custom JS script is an inject.js file exists in the working directory.<commit_after>'use strict';

var asynEach = require('async-foreach').forEach,
    chalk = require('chalk'),
    path = require('path'),
    phantom = require('phantom'),
    Q = require('q'),
    utils = require('./utils');

var screenshotExclusions = ['pdf'];

/**
 * Loops through defined viewports, rendering passed url, and taking screenshots using PhantomJS
 * @author Steven Britton
 * @date   2016-04-14
 * @param  {[type]}   options [description]
 * @param  {String}   url     [description]
 * @param  {Function} done    [description]
 * @return {[type]}           [description]
 */
function renderPage(options, url, done) {

    utils.log.verbose('Screenshot: renderPage');

    var folder = utils.urlToDir(url),
        phantomOptions=[],
        phantomOutObj,
        phantomOutObj2,
        viewports = options.viewports,
        output, key;

    function handleMsgStack(msgStack){

        if(Array.isArray(msgStack) && msgStack.length > 0){
            console.log('\n    ', chalk.red.bold(msgStack.join('\n     ')), '\n');
        }
    }

    if(options.sitecrawler_options.ignoreInvalidSSL){
        phantomOptions.push('--ignore-ssl-errors=yes');
    }

    phantom.create(phantomOptions).then(function(ph) {

        ph.createPage().then(function(page) {

            page.setting('userAgent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36');

            // do we have stored credentials?
            if (options.domain.auth.user !== null && options.domain.auth.pwd !== null) {

                utils.log.verbose('\n   ⤷  Setting Authentication');

                page.setting('userName', options.domain.auth.user);
                page.setting('password', options.domain.auth.pwd);
            }

            // Create an Output object to pass through Phantom since PhantomJS does not share any memory or variables with node
            phantomOutObj = ph.createOutObject();
            phantomOutObj2 = ph.createOutObject();

            // Create property to tracking messages while working with PhantomJS
            phantomOutObj.msgStackJS = [];
            phantomOutObj2.msgStackResources = [];

            /**
             * Callback function that's invoked when there is a JavaScript execution error
             * @author Steven Britton
             * @date   2016-05-12
             * @param  {String}   msg           error messages
             * @param  {Object}   trace         error trace
             * @param  {Array}    out           captures and returns error messages
             * @param  {Object}   phantomOutObj
             * @return {Object}   phantomOutObj
             */
            page.property('onError', function(msg, trace, out) {

                out.msgStackJS.push('');
                out.msgStackJS.push('JS ERROR:');
                out.msgStackJS.push('');
                out.msgStackJS.push(msg);

                if (trace && trace.length) {
                    trace.forEach(function(t) {
                        out.msgStackJS.push('File: ' + t.file);
                        out.msgStackJS.push('Line: ' + t.line);
                        out.msgStackJS.push('Function: ' + t.function);
                    });
                }
            }, phantomOutObj);

            /**
             * Callback function that's invoked when there is a resource load error
             * @author Steven Britton
             * @date   2016-05-12
             * @param  {String}   msg           error messages
             * @param  {Object}   trace         error trace
             * @param  {Array}    out           captures and returns error messages
             * @param  {Object}   phantomOutObj
             * @return {Object}   phantomOutObj
             */

            page.property('onResourceError', function(resourceError, out) {

                out.msgStackResources.push('');
                out.msgStackResources.push('ResourceError: ' + resourceError.url);
                out.msgStackResources.push('Error code: ' + resourceError.errorCode);
                out.msgStackResources.push('Error Description: ' + resourceError.errorString);

            }, phantomOutObj2);

            page.property('onConsoleMessage', function(msg){
                console.log('');
                console.log('');
                console.log('    ', msg);
                console.log('');
                console.log('');
            });

            /**
             * PhantonJS Page Open function
             * @author Steven Britton
             */
            page.open(url).then(function(status) {

                function renderView(n) {

                    if (!!n) {
                        key = n - 1;

                        console.log(chalk.green.bold('    ✔  Viewport:'), chalk.green(viewports[key].width + 'x' + viewports[key].height));

                        page.property('viewportSize', { 'width': viewports[key].width, 'height': viewports[key].height });

                        output = path.join(options.paths.output, folder, viewports[key].width + '.png');

                        if ( status === 'success' ) {
                            page.injectJs('inject.js');
                        }

                        setTimeout(function() {
                            page.render(output).then(function() {

                                renderView(key);

                            });
                        }, options.screenshot.delay);

                    } else {

                        ph.exit(0);

                        done();
                    }
                }

                if (status === 'success') {

                    console.log('\n   ', chalk.underline.yellow(folder));

                    renderView(viewports.length);

                } else {
                    ph.exit(1);

                    return done('Failed connection: ' + url);
                }
            });

            /**
             * Callback functions for custom output properties
             * @author Steven Britton
             */
            phantomOutObj.property('msgStackJS').then(function(msgStack) {

                handleMsgStack(msgStack);
            });

             phantomOutObj2.property('msgStackResources').then(function(msgStack) {
                handleMsgStack(msgStack);
            });

        });
    });
}

/**
 * Screenshot Object
 * @author Steven Britton
 * @date   2016-04-14
 * @param  {Array}   pages   list of links passed from sitemap.xml file
 * @param  {Object}  options [description]
 */
function Screenshot(parameters) {

    utils.log.verbose('Screenshot: constructor', parameters);

    console.log(chalk.yellow.bold(' ⤷ Generating screenshots'));

    this.pages = parameters.pages;
    this.options = parameters.options;

    var exclusions, regExclusions;

    // format exclusions
    exclusions = screenshotExclusions.join('|');

    // regular expression for exclusions
    regExclusions = new RegExp('\.(' + exclusions + ')', 'i');

    // filter screenshotExclusions
    this.pages = this.pages.filter(function(page) {
        return !page.match(regExclusions);
    });

}

Screenshot.prototype.start = function() {

    utils.log.verbose('Screenshot: start');

    var d = Q.defer(),
        options = this.options;

    asynEach(this.pages, function(page) {

        utils.log.verbose('Screenshot: start asynEach', page);

        var done = this.async();

        renderPage(options, page, function(err) {

            if (err) {
                utils.log.error(err);
            }

            done();
        });

    }, function(success, array) {

        if (success) {
            d.resolve(array);

        } else {
            d.reject(success);
        }

    });

    return d.promise;
};

module.exports = Screenshot;
<|endoftext|>