<commit_before>/**
 * Module dependencies.
 */
var uri = require('url'),
  crypto = require('crypto'),
  util = require('util'),
  OAuth2Strategy = require('passport-oauth2'),
  Profile = require('./profile'),
  InternalOAuthError = require('passport-oauth2').InternalOAuthError,
  FacebookAuthorizationError = require('./errors/facebookauthorizationerror'),
  FacebookTokenError = require('./errors/facebooktokenerror'),
  FacebookGraphAPIError = require('./errors/facebookgraphapierror'),
  url = require('url'),
  OAuth2 = require('oauth').OAuth2;

/**
 * `Strategy` constructor.
 *
 * The Facebook authentication strategy authenticates requests by delegating to
 * Facebook using the OAuth 2.0 protocol.
 *
 * Applications must supply a `verify` callback which accepts an `accessToken`,
 * `refreshToken` and service-specific `profile`, and then calls the `done`
 * callback supplying a `user`, which should be set to `false` if the
 * credentials are not valid.  If an exception occured, `err` should be set.
 *
 * Options:
 *   - `clientID`      your Facebook application's App ID
 *   - `clientSecret`  your Facebook application's App Secret
 *   - `callbackURL`   URL to which Facebook will redirect the user after granting authorization
 *
 * Examples:
 *
 *     passport.use(new FacebookStrategy({
 *         clientID: '123-456-789',
 *         clientSecret: 'shhh-its-a-secret'
 *         callbackURL: 'https://www.example.net/auth/facebook/callback'
 *       },
 *       function(accessToken, refreshToken, profile, done) {
 *         User.findOrCreate(..., function (err, user) {
 *           done(err, user);
 *         });
 *       }
 *     ));
 *
 * @param {Object} options
 * @param {Function} verify
 * @api public
 */
function Strategy(options, verify) {
  options = options || {};

  options.authorizationURL = options.authorizationURL || 'https://' + options.slug +
    '.nationbuilder.com/oauth/authorize';
  options.tokenURL = options.tokenURL || 'https://' + options.slug +
    '.nationbuilder.com/oauth/token';
  options.scopeSeparator = options.scopeSeparator || ',';

  OAuth2Strategy.call(this, options, verify);
  this.name = 'nationbuilder';
  this._slug = options.slug;
  this._clientID = options.clientID;
  this._clientSecret = options.clientSecret;
}

/**
 * Inherit from `OAuth2Strategy`.
 */
util.inherits(Strategy, OAuth2Strategy);

OAuth2Strategy.prototype._getOAuth = function(slug) {
  var self = this;

  var authorizationURL = 'https://' + slug +
    '.nationbuilder.com/oauth/authorize',
    tokenURL = 'https://' + slug +
    '.nationbuilder.com/oauth/token';
  // NOTE: The _oauth2 property is considered "protected".  Subclasses are
  //       allowed to use it when making protected resource requests to retrieve
  //       the user profile.
  return new OAuth2(self._clientID, self._clientSecret,
    '', authorizationURL, tokenURL, undefined);
}

/**
 * Authenticate request by delegating to a service provider using OAuth 2.0.
 *
 * @param {Object} req
 * @api protected
 */
OAuth2Strategy.prototype.authenticate = function(req, options) {
  options = options || {};
  var self = this;

  if (options.slug || req.session.slug) {

    if (!options.slug && req.session.slug) {
      options.slug = req.session.slug;
    }

    var _oauth2 = self._getOAuth(options.slug);

  }

  if (req.query && req.query.error) {
    if (req.query.error == 'access_denied') {
      return this.fail({
        message: req.query.error_description
      });
    } else {
      return this.error(new AuthorizationError(req.query.error_description, req.query.error, req.query.error_uri));
    }
  }

  var callbackURL = options.callbackURL || this._callbackURL;
  if (callbackURL) {
    var parsed = url.parse(callbackURL);
    if (!parsed.protocol) {
      // The callback URL is relative, resolve a fully qualified URL from the
      // URL of the originating request.
      callbackURL = url.resolve(utils.originalURL(req, {
        proxy: this._trustProxy
      }), callbackURL);
    }
  }

  if (req.query && req.query.code) {
    var code = req.query.code;

    if (this._state) {
      if (!req.session) {
        return this.error(new Error('OAuth2Strategy requires session support when using state. Did you forget app.use(express.session(...))?'));
      }

      var key = this._key;
      if (!req.session[key]) {
        return this.fail({
          message: 'Unable to verify authorization request state.'
        }, 403);
      }
      var state = req.session[key].state;
      if (!state) {
        return this.fail({
          message: 'Unable to verify authorization request state.'
        }, 403);
      }

      delete req.session[key].state;
      if (Object.keys(req.session[key]).length === 0) {
        delete req.session[key];
      }

      if (state !== req.query.state) {
        return this.fail({
          message: 'Invalid authorization request state.'
        }, 403);
      }
    }

    var params = this.tokenParams(options);
    params.grant_type = 'authorization_code';
    params.redirect_uri = callbackURL;

    (_oauth2 || this._oauth2).getOAuthAccessToken(code, params,
      function(err, accessToken, refreshToken, params) {
        if (err) {
          return self.error(self._createOAuthError('Failed to obtain access token', err));
        }

        self._loadUserProfile(options.slug || self._slug, accessToken, function(err, profile) {
          if (err) {
            return self.error(err);
          }

          function verified(err, user, info) {
            if (err) {
              return self.error(err);
            }
            if (!user) {
              return self.fail(info);
            }
            self.success(user, info);
          }

          try {
            if (self._passReqToCallback) {
              var arity = self._verify.length;
              if (arity == 6) {
                self._verify(req, accessToken, refreshToken, params, profile, verified);
              } else { // arity == 5
                self._verify(req, accessToken, refreshToken, profile, verified);
              }
            } else {
              var arity = self._verify.length;
              if (arity == 5) {
                self._verify(accessToken, refreshToken, params, profile, verified);
              } else { // arity == 4
                self._verify(accessToken, refreshToken, profile, verified);
              }
            }
          } catch (ex) {
            return self.error(ex);
          }
        });
      }
    );
  } else {
    var params = this.authorizationParams(options);
    params.response_type = 'code';
    params.redirect_uri = callbackURL;
    var scope = options.scope || this._scope;
    if (scope) {
      if (Array.isArray(scope)) {
        scope = scope.join(this._scopeSeparator);
      }
      params.scope = scope;
    }
    var state = options.state;
    if (state) {
      params.state = state;
    } else if (this._state) {
      if (!req.session) {
        return this.error(new Error('OAuth2Strategy requires session support when using state. Did you forget app.use(express.session(...))?'));
      }

      var key = this._key;
      state = uid(24);
      if (!req.session[key]) {
        req.session[key] = {};
      }
      req.session[key].state = state;
      params.state = state;
    }

    var location = (_oauth2 || this._oauth2).getAuthorizeUrl(params);
    this.redirect(location);
  }
};

/**
 * Return extra Facebook-specific parameters to be included in the authorization
 * request.
 *
 * Options:
 *  - `display`  Display mode to render dialog, { `page`, `popup`, `touch` }.
 *
 * @param {Object} options
 * @return {Object}
 * @api protected
 */
Strategy.prototype.authorizationParams = function(options) {
  var params = {};

  // https://developers.facebook.com/docs/reference/dialogs/oauth/
  if (options.display) {
    params.display = options.display;
  }

  // https://developers.facebook.com/docs/facebook-login/reauthentication/
  if (options.authType) {
    params.auth_type = options.authType;
  }
  if (options.authNonce) {
    params.auth_nonce = options.authNonce;
  }

  return params;
};

/**
 * Load user profile, contingent upon options.
 *
 * @param {String} accessToken
 * @param {Function} done
 * @api private
 */
OAuth2Strategy.prototype._loadUserProfile = function(slug, accessToken, done) {
  var self = this;

  function loadIt() {
    return self.userProfile(slug, accessToken, done);
  }

  function skipIt() {
    return done(null);
  }

  if (typeof this._skipUserProfile == 'function' && this._skipUserProfile.length > 1) {
    // async
    this._skipUserProfile(accessToken, function(err, skip) {
      if (err) {
        return done(err);
      }
      if (!skip) {
        return loadIt();
      }
      return skipIt();
    });
  } else {
    var skip = (typeof this._skipUserProfile == 'function') ? this._skipUserProfile() : this._skipUserProfile;
    if (!skip) {
      return loadIt();
    }
    return skipIt();
  }
};

/**
 * Retrieve user profile from Facebook.
 *
 * This function constructs a normalized profile, with the following properties:
 *
 *   - `provider`         always set to `facebook`
 *   - `id`               the user's Facebook ID
 *   - `username`         the user's Facebook username
 *   - `displayName`      the user's full name
 *   - `name.familyName`  the user's last name
 *   - `name.givenName`   the user's first name
 *   - `name.middleName`  the user's middle name
 *   - `gender`           the user's gender: `male` or `female`
 *   - `profileUrl`       the URL of the profile for the user on Facebook
 *   - `emails`           the proxied or contact email address granted by the user
 *
 * @param {String} accessToken
 * @param {Function} done
 * @api protected
 */
Strategy.prototype.userProfile = function(slug, accessToken, done) {
  var url = uri.parse('https://' + slug +
    '.nationbuilder.com/api/v1/people/me');
  url = uri.format(url);
  this.get(slug, url, accessToken, function(err, body, res) {
    var json;

    if (err) {
      if (err.data) {
        try {
          json = JSON.parse(err.data);
        } catch (_) {}
      }

      if (json && json.error && typeof json.error == 'object') {
        return done(new FacebookGraphAPIError(json.error.message, json.error.type,
          json.error.code, json.error.error_subcode));
      }
      return done(new InternalOAuthError('Failed to fetch user profile', err));
    }

    try {
      json = JSON.parse(body);
    } catch (ex) {
      return done(new Error('Failed to parse user profile'));
    }

    var profile = Profile.parse(json);
    profile.provider = 'nationbuilder';
    profile._raw = body;
    profile._json = json;
    profile._accessToken = accessToken;
    profile.nation = slug;

    done(null, profile);
  });
};

/**
 * Cannot use this._oauth2.get because we need to modify Accept and Content-Type
 *  headers
 */
Strategy.prototype.get = function(slug, url, accessToken, done) {
  var self = this;
  var headers = {
    'Authorization': this._oauth2.buildAuthHeader(accessToken),
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  };

  var _oauth2 = self._getOAuth(slug);
  _oauth2._request("GET", url, headers, "", null, done);
}

/**
 * Parse error response from Facebook OAuth 2.0 token endpoint.
 *
 * @param {String} body
 * @param {Number} status
 * @return {Error}
 * @api protected
 */
Strategy.prototype.parseErrorResponse = function(body, status) {
  var json = JSON.parse(body);
  if (json.error && typeof json.error == 'object') {
    return new FacebookTokenError(json.error.message, json.error.type, json.error
      .code, json.error.error_subcode);
  }
  return OAuth2Strategy.prototype.parseErrorResponse.call(this, body, status);
};

/**
 * Expose `Strategy`.
 */
module.exports = Strategy;
<commit_msg>Set session if slug is used<commit_after>/**
 * Module dependencies.
 */
var uri = require('url'),
  crypto = require('crypto'),
  util = require('util'),
  OAuth2Strategy = require('passport-oauth2'),
  Profile = require('./profile'),
  InternalOAuthError = require('passport-oauth2').InternalOAuthError,
  FacebookAuthorizationError = require('./errors/facebookauthorizationerror'),
  FacebookTokenError = require('./errors/facebooktokenerror'),
  FacebookGraphAPIError = require('./errors/facebookgraphapierror'),
  url = require('url'),
  OAuth2 = require('oauth').OAuth2;

/**
 * `Strategy` constructor.
 *
 * The Facebook authentication strategy authenticates requests by delegating to
 * Facebook using the OAuth 2.0 protocol.
 *
 * Applications must supply a `verify` callback which accepts an `accessToken`,
 * `refreshToken` and service-specific `profile`, and then calls the `done`
 * callback supplying a `user`, which should be set to `false` if the
 * credentials are not valid.  If an exception occured, `err` should be set.
 *
 * Options:
 *   - `clientID`      your Facebook application's App ID
 *   - `clientSecret`  your Facebook application's App Secret
 *   - `callbackURL`   URL to which Facebook will redirect the user after granting authorization
 *
 * Examples:
 *
 *     passport.use(new FacebookStrategy({
 *         clientID: '123-456-789',
 *         clientSecret: 'shhh-its-a-secret'
 *         callbackURL: 'https://www.example.net/auth/facebook/callback'
 *       },
 *       function(accessToken, refreshToken, profile, done) {
 *         User.findOrCreate(..., function (err, user) {
 *           done(err, user);
 *         });
 *       }
 *     ));
 *
 * @param {Object} options
 * @param {Function} verify
 * @api public
 */
function Strategy(options, verify) {
  options = options || {};

  options.authorizationURL = options.authorizationURL || 'https://' + options.slug +
    '.nationbuilder.com/oauth/authorize';
  options.tokenURL = options.tokenURL || 'https://' + options.slug +
    '.nationbuilder.com/oauth/token';
  options.scopeSeparator = options.scopeSeparator || ',';

  OAuth2Strategy.call(this, options, verify);
  this.name = 'nationbuilder';
  this._slug = options.slug;
  this._clientID = options.clientID;
  this._clientSecret = options.clientSecret;
}

/**
 * Inherit from `OAuth2Strategy`.
 */
util.inherits(Strategy, OAuth2Strategy);

OAuth2Strategy.prototype._getOAuth = function(slug) {
  var self = this;

  var authorizationURL = 'https://' + slug +
    '.nationbuilder.com/oauth/authorize',
    tokenURL = 'https://' + slug +
    '.nationbuilder.com/oauth/token';
  // NOTE: The _oauth2 property is considered "protected".  Subclasses are
  //       allowed to use it when making protected resource requests to retrieve
  //       the user profile.
  return new OAuth2(self._clientID, self._clientSecret,
    '', authorizationURL, tokenURL, undefined);
}

/**
 * Authenticate request by delegating to a service provider using OAuth 2.0.
 *
 * @param {Object} req
 * @api protected
 */
OAuth2Strategy.prototype.authenticate = function(req, options) {
  options = options || {};
  var self = this;

  if (options.slug || req.session.slug) {

    if (options.slug) {
      req.session.slug = options.slug;
    }

    if (!options.slug && req.session.slug) {
      options.slug = req.session.slug;
    }

    var _oauth2 = self._getOAuth(options.slug);

  }

  if (req.query && req.query.error) {
    if (req.query.error == 'access_denied') {
      return this.fail({
        message: req.query.error_description
      });
    } else {
      return this.error(new AuthorizationError(req.query.error_description, req.query.error, req.query.error_uri));
    }
  }

  var callbackURL = options.callbackURL || this._callbackURL;
  if (callbackURL) {
    var parsed = url.parse(callbackURL);
    if (!parsed.protocol) {
      // The callback URL is relative, resolve a fully qualified URL from the
      // URL of the originating request.
      callbackURL = url.resolve(utils.originalURL(req, {
        proxy: this._trustProxy
      }), callbackURL);
    }
  }

  if (req.query && req.query.code) {
    var code = req.query.code;

    if (this._state) {
      if (!req.session) {
        return this.error(new Error('OAuth2Strategy requires session support when using state. Did you forget app.use(express.session(...))?'));
      }

      var key = this._key;
      if (!req.session[key]) {
        return this.fail({
          message: 'Unable to verify authorization request state.'
        }, 403);
      }
      var state = req.session[key].state;
      if (!state) {
        return this.fail({
          message: 'Unable to verify authorization request state.'
        }, 403);
      }

      delete req.session[key].state;
      if (Object.keys(req.session[key]).length === 0) {
        delete req.session[key];
      }

      if (state !== req.query.state) {
        return this.fail({
          message: 'Invalid authorization request state.'
        }, 403);
      }
    }

    var params = this.tokenParams(options);
    params.grant_type = 'authorization_code';
    params.redirect_uri = callbackURL;

    (_oauth2 || this._oauth2).getOAuthAccessToken(code, params,
      function(err, accessToken, refreshToken, params) {
        if (err) {
          return self.error(self._createOAuthError('Failed to obtain access token', err));
        }

        self._loadUserProfile(options.slug || self._slug, accessToken, function(err, profile) {
          if (err) {
            return self.error(err);
          }

          function verified(err, user, info) {
            if (err) {
              return self.error(err);
            }
            if (!user) {
              return self.fail(info);
            }
            self.success(user, info);
          }

          try {
            if (self._passReqToCallback) {
              var arity = self._verify.length;
              if (arity == 6) {
                self._verify(req, accessToken, refreshToken, params, profile, verified);
              } else { // arity == 5
                self._verify(req, accessToken, refreshToken, profile, verified);
              }
            } else {
              var arity = self._verify.length;
              if (arity == 5) {
                self._verify(accessToken, refreshToken, params, profile, verified);
              } else { // arity == 4
                self._verify(accessToken, refreshToken, profile, verified);
              }
            }
          } catch (ex) {
            return self.error(ex);
          }
        });
      }
    );
  } else {
    var params = this.authorizationParams(options);
    params.response_type = 'code';
    params.redirect_uri = callbackURL;
    var scope = options.scope || this._scope;
    if (scope) {
      if (Array.isArray(scope)) {
        scope = scope.join(this._scopeSeparator);
      }
      params.scope = scope;
    }
    var state = options.state;
    if (state) {
      params.state = state;
    } else if (this._state) {
      if (!req.session) {
        return this.error(new Error('OAuth2Strategy requires session support when using state. Did you forget app.use(express.session(...))?'));
      }

      var key = this._key;
      state = uid(24);
      if (!req.session[key]) {
        req.session[key] = {};
      }
      req.session[key].state = state;
      params.state = state;
    }

    var location = (_oauth2 || this._oauth2).getAuthorizeUrl(params);
    this.redirect(location);
  }
};

/**
 * Return extra Facebook-specific parameters to be included in the authorization
 * request.
 *
 * Options:
 *  - `display`  Display mode to render dialog, { `page`, `popup`, `touch` }.
 *
 * @param {Object} options
 * @return {Object}
 * @api protected
 */
Strategy.prototype.authorizationParams = function(options) {
  var params = {};

  // https://developers.facebook.com/docs/reference/dialogs/oauth/
  if (options.display) {
    params.display = options.display;
  }

  // https://developers.facebook.com/docs/facebook-login/reauthentication/
  if (options.authType) {
    params.auth_type = options.authType;
  }
  if (options.authNonce) {
    params.auth_nonce = options.authNonce;
  }

  return params;
};

/**
 * Load user profile, contingent upon options.
 *
 * @param {String} accessToken
 * @param {Function} done
 * @api private
 */
OAuth2Strategy.prototype._loadUserProfile = function(slug, accessToken, done) {
  var self = this;

  function loadIt() {
    return self.userProfile(slug, accessToken, done);
  }

  function skipIt() {
    return done(null);
  }

  if (typeof this._skipUserProfile == 'function' && this._skipUserProfile.length > 1) {
    // async
    this._skipUserProfile(accessToken, function(err, skip) {
      if (err) {
        return done(err);
      }
      if (!skip) {
        return loadIt();
      }
      return skipIt();
    });
  } else {
    var skip = (typeof this._skipUserProfile == 'function') ? this._skipUserProfile() : this._skipUserProfile;
    if (!skip) {
      return loadIt();
    }
    return skipIt();
  }
};

/**
 * Retrieve user profile from Facebook.
 *
 * This function constructs a normalized profile, with the following properties:
 *
 *   - `provider`         always set to `facebook`
 *   - `id`               the user's Facebook ID
 *   - `username`         the user's Facebook username
 *   - `displayName`      the user's full name
 *   - `name.familyName`  the user's last name
 *   - `name.givenName`   the user's first name
 *   - `name.middleName`  the user's middle name
 *   - `gender`           the user's gender: `male` or `female`
 *   - `profileUrl`       the URL of the profile for the user on Facebook
 *   - `emails`           the proxied or contact email address granted by the user
 *
 * @param {String} accessToken
 * @param {Function} done
 * @api protected
 */
Strategy.prototype.userProfile = function(slug, accessToken, done) {
  var url = uri.parse('https://' + slug +
    '.nationbuilder.com/api/v1/people/me');
  url = uri.format(url);
  this.get(slug, url, accessToken, function(err, body, res) {
    var json;

    if (err) {
      if (err.data) {
        try {
          json = JSON.parse(err.data);
        } catch (_) {}
      }

      if (json && json.error && typeof json.error == 'object') {
        return done(new FacebookGraphAPIError(json.error.message, json.error.type,
          json.error.code, json.error.error_subcode));
      }
      return done(new InternalOAuthError('Failed to fetch user profile', err));
    }

    try {
      json = JSON.parse(body);
    } catch (ex) {
      return done(new Error('Failed to parse user profile'));
    }

    var profile = Profile.parse(json);
    profile.provider = 'nationbuilder';
    profile._raw = body;
    profile._json = json;
    profile._accessToken = accessToken;
    profile.nation = slug;

    done(null, profile);
  });
};

/**
 * Cannot use this._oauth2.get because we need to modify Accept and Content-Type
 *  headers
 */
Strategy.prototype.get = function(slug, url, accessToken, done) {
  var self = this;
  var headers = {
    'Authorization': this._oauth2.buildAuthHeader(accessToken),
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  };

  var _oauth2 = self._getOAuth(slug);
  _oauth2._request("GET", url, headers, "", null, done);
}

/**
 * Parse error response from Facebook OAuth 2.0 token endpoint.
 *
 * @param {String} body
 * @param {Number} status
 * @return {Error}
 * @api protected
 */
Strategy.prototype.parseErrorResponse = function(body, status) {
  var json = JSON.parse(body);
  if (json.error && typeof json.error == 'object') {
    return new FacebookTokenError(json.error.message, json.error.type, json.error
      .code, json.error.error_subcode);
  }
  return OAuth2Strategy.prototype.parseErrorResponse.call(this, body, status);
};

/**
 * Expose `Strategy`.
 */
module.exports = Strategy;
<|endoftext|>


<commit_before>var eventsource = require('./eventsource/amqp');

module.exports = function streamer(req, res, next) {
  console.log('Event Source opened (Exchange: "' + req.params.exchange + '" Key: "' + req.params.routing_key + '")');

  // let request last as long as possible
  req.socket.setTimeout(Infinity);

  // send headers for event-stream connection
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive'
  });
  res.write('\n');

  var messageCount = 0;

  eventsource.then(function(mq) {
    console.log('Client Connected to AMQP');
    var queue_name = 'dlindahl-test-' + req.sessionID;
    mq.queue(queue_name, { durable:false, autoDelete:true }, function(q) {
      var ctag,
          key = req.params.routing_key.replace(/\*\*/g, '#');

      // Catch all messages
      q.bind(req.params.exchange, key);

      // Receive messages
      q.subscribe(function (message, headers, deliveryInfo) {
        console.log('AMQP MESSAGE');
        console.log('     ' + message.data.toString());
        console.log('     ' + deliveryInfo.routingKey);
        console.log('\n');

        messageCount++;

        res.write('{');
        res.write('event: ' + deliveryInfo.routingKey + '\n');
        res.write('id: '    + messageCount + '\n');
        res.write('data: '  + message.data.toString() + '\n\n'); // Note the extra newline
        res.write('}');
      }).addCallback(function(ok) {
        console.log('Client connected to Queue (' + queue_name + ')');
        ctag = ok.consumerTag;
      });

      req.connection.once('close', function() {
        if(ctag) {
          q.unsubscribe(ctag);
        }
      });
    });

  }, function() {
    console.log('AMQP error');
    res.end();
  });

  // The 'close' event is fired when a user closes their browser window.
  // In that situation we want to make sure our redis channel subscription
  // is properly shut down to prevent memory leaks...and incorrect subscriber
  // counts to the channel.
  req.connection.on('close', function() {
    console.log('Event Source closed');
    req.connection.removeAllListeners();
  });
};<commit_msg>Breaks streamer logic into stand-alone functions/callbacks<commit_after>var eventsource = require('./eventsource/amqp');

var onMessage = function(res) {
  var messageCount = 0;
  return function(message, headers, deliveryInfo) {
    console.log('AMQP MESSAGE');
    console.log('     ' + deliveryInfo.routingKey);
    console.log('     ' + message.data.toString());
    console.log('\n');

    messageCount++;

    res.write('{');
    res.write('event: ' + deliveryInfo.routingKey + '\n');
    res.write('id: '    + messageCount + '\n');
    res.write('data: '  + message.data.toString() + '\n\n'); // Note the extra newline
    res.write('}');
  };
};

var onSubscribe = function(req, queue_name) {
  return function(subscription) {
    console.log('Client connected to Queue (' + queue_name + ')');
    req.session.ctag = subscription.consumerTag;
  };
};

var unsubscribeOnClose = function(req, q) {
  return function() {
    if(req.session.ctag) {
      q.unsubscribe(req.session.ctag);
    }
  };
};

var onQueue = function(req, res, queue_name) {
  return function(q) {
    q.bind(req.params.exchange, req.params.routing_key);

    // Receive messages
    q.subscribe(onMessage(res)).addCallback(onSubscribe(req, queue_name));

    req.connection.once('close', unsubscribeOnClose(req, q));
  };
};

var onConnect = function(req, res) {
  return function(mq) {
    console.log('Client Connected to AMQP');
    var queue_name = 'dlindahl-test-' + req.sessionID;
        opts = { durable:false, autoDelete:true };

    mq.queue(queue_name, opts, onQueue(req, res, queue_name));
  };
};

var onSourceError = function(res) {
  return function() {
    console.log('AMQP error');
    res.end();
  };
};

module.exports = function streamer(req, res, next) {
  console.log('Event Source opened (Exchange: "' + req.params.exchange + '" Key: "' + req.params.routing_key + '")');

  // let request last as long as possible
  req.socket.setTimeout(Infinity);

  // send headers for event-stream connection
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive'
  });
  res.write('\n');

  eventsource.then(onConnect(req, res), onSourceError(res));

  // The 'close' event is fired when a user closes their browser window.
  // In that situation we want to make sure our redis channel subscription
  // is properly shut down to prevent memory leaks...and incorrect subscriber
  // counts to the channel.
  req.connection.on('close', function() {
    console.log('Event Source closed');
    req.connection.removeAllListeners();
  });
};<|endoftext|>


<commit_before>'use strict';

var Writable = require('stream').Writable;
var Parser = require('./parser.js');

function Streamer(options) {
  Writable.call(this, options);
  this.buffer = '';
  this.sep = (options && options.separator) ? options.separator : undefined;
  this.quo = (options && options.quote) ? options.quote : undefined;
}

Streamer.prototype = Object.create(
  Writable.prototype, { constructor: { value: Streamer }});

Streamer.prototype._write = function (chunk, encoding, done) {

  this.buffer = this.buffer.concat(chunk.toString());

  if (this.sep === undefined) {
    // try to detect the separator if not provided
    this.sep = require('./csv.js').detect(this.buffer);
  }

  var csv = new Parser(this.buffer, this.sep, this.quo);
  var rows = csv.File();

  if (csv.pointer > this.buffer.length) {
    this.buffer = this.buffer.slice(csv.pointer);
  }
  else {
    rows.pop();
    this.buffer = this.buffer.slice(csv.linePointer);
  }
  if (rows.length > 0) {
    var self = this;
    rows.forEach(function (row) {
        self.emit('data', row);
      }
    )
  }
  done();
}

Streamer.prototype.end = function () {
  var csv = new Parser(this.buffer, this.sep, this.quo);
  var rows = csv.File();
  if (rows.length > 0) {
    var self = this;
    rows.forEach(function (row) {
        self.emit('data', row);
      }
    )
  }
  this.emit('end');
};

module.exports = Streamer;
<commit_msg>bugfix: when a chunk finished at the end of one line<commit_after>'use strict';

var Writable = require('stream').Writable;
var Parser = require('./parser.js');

function Streamer(options) {
  Writable.call(this, options);
  this.buffer = '';
  this.sep = (options && options.separator) ? options.separator : undefined;
  this.quo = (options && options.quote) ? options.quote : undefined;
}

Streamer.prototype = Object.create(
  Writable.prototype, { constructor: { value: Streamer }});

Streamer.prototype._write = function (chunk, encoding, done) {

  this.buffer = this.buffer.concat(chunk.toString());

  if (this.sep === undefined) {
    // try to detect the separator if not provided
    this.sep = require('./csv.js').detect(this.buffer);
  }

  var csv = new Parser(this.buffer, this.sep, this.quo);
  var rows = csv.File();

  if (csv.pointer > this.buffer.length) {
    this.buffer = this.buffer.slice(csv.pointer);
  }
  else {
    if (csv.pointer === this.buffer.length) {
      rows.pop();
    }
    this.buffer = this.buffer.slice(csv.linePointer);
  }
  if (rows.length > 0) {
    var self = this;
    rows.forEach(function (row) {
        self.emit('data', row);
      }
    )
  }
  done();
}

Streamer.prototype.end = function () {
  var csv = new Parser(this.buffer, this.sep, this.quo);
  var rows = csv.File();
  if (rows.length > 0) {
    var self = this;
    rows.forEach(function (row) {
        self.emit('data', row);
      }
    )
  }
  this.emit('end');
};

module.exports = Streamer;
<|endoftext|>


<commit_before>'use strict';

function getOptimalCodeLengths(weights) {
  var nodes =
    weights
    .map(function(w, i) { return { w: w, i: i }})
    .filter(function(n) { return n.w > 0; });
  nodes.sort(function(a, b) {
    if (a.w < b.w) return -1;
    if (a.w > b.w) return +1;
    return 0;
  });
  var q1 = nodes;
  var q2 = [];
  function shiftMin() {
    if (q1.length == 0) return q2.shift();
    if (q2.length == 0) return q1.shift();
    return (q1[0].w < q2[0].w) ? q1.shift() : q2.shift();
  }
  while (q1.length + q2.length > 1) {
    var n1 = shiftMin();
    var n2 = shiftMin();
    var n = { w: n1.w + n2.w, l: n1, r: n2 };
    q2.push(n);
  }
  var root = (q1.length > 0) ? q1[0] : q2[0];
  var codeLengths = [];
  function traverseDFS(n, d) {
    if (n.l) traverseDFS(n.l, d + 1);
    if (n.r) traverseDFS(n.r, d + 1);
    if ('i' in n) codeLengths[n.i] = d;
  }
  traverseDFS(root, 1);
  return codeLengths;
}

function codeLengthsToCanonicalEncoding(codeLengths) {
  var nodes =
    codeLengths
    .map(function(l, i) { return { l: l, i: i }});
  nodes.sort(function(a, b) {
    if (a.l < b.l) return -1;
    if (a.l > b.l) return +1;
    if (a.i < b.i) return -1;
    if (a.i > b.i) return +1;
    return 0;
  });
  if (nodes.length > 0) {
    nodes[0].code = 0;
    for (var i = 1; i < nodes.length; ++i) {
      nodes[i].code = (nodes[i-1].code + 1) << (nodes[i].l - nodes[i - 1].l);
    }
  }
  var codes = [];
  for (var i = 0; i < nodes.length; ++i) {
    var j = nodes[i].i;
    delete nodes[i].i;
    codes[j] = nodes[i];
  }
  return codes;
}

function encodeASCII(str, codebook) {
  var a = [];
  var aByte = 0;
  var aByteBitLength = 0;
  function appendCode(code, l) {
    for (var i = 0; i < l; ++i) {
      var bit = (code & (1 << (l - i - 1))) ? 1 : 0;
      aByte = (aByte << 1) | bit;
      ++aByteBitLength;
      if (aByteBitLength == 8) {
        a.push(aByte);
        aByte = 0;
        aByteBitLength = 0;
      }
    }
  }
  function finishCoding() {
    var eofCode = codebook[256];
    appendCode(eofCode.code, eofCode.l);
    if (aByteBitLength > 0) {
      aByte <<= (8 - aByteBitLength);
      a.push(aByte);
    }
  }
  for (var i = 0; i < str.length; ++i) {
    var ch = str.charCodeAt(i);
    var code = codebook[ch];
    appendCode(code.code, code.l);
  }
  finishCoding();
  return a;
}

function decodeASCII(a, inverseCodebook) {
  var code = 0;
  var codeLength = 0;
  var str = '';
  for (var i = 0; i < a.length * 8; ++i) {
    var bit = (a[(i / 8) >>> 0] & (1 << (7 - (i % 8)))) ? 1 : 0;
    code = (code << 1) | bit;
    ++codeLength;
    if (code >= inverseCodebook.length) {
      throw new Error('Invalid code ' + code);
    }
    if (code in inverseCodebook && inverseCodebook[code].l == codeLength) {
      var ch = inverseCodebook[code].i;
      code = 0;
      codeLength = 0;
      if (ch == 256) {
        break;
      }
      str += String.fromCharCode(ch);
    }
  }
  return str;
}
<commit_msg>Make decodeASCII return extra information<commit_after>'use strict';

function getOptimalCodeLengths(weights) {
  var nodes =
    weights
    .map(function(w, i) { return { w: w, i: i }})
    .filter(function(n) { return n.w > 0; });
  nodes.sort(function(a, b) {
    if (a.w < b.w) return -1;
    if (a.w > b.w) return +1;
    return 0;
  });
  var q1 = nodes;
  var q2 = [];
  function shiftMin() {
    if (q1.length == 0) return q2.shift();
    if (q2.length == 0) return q1.shift();
    return (q1[0].w < q2[0].w) ? q1.shift() : q2.shift();
  }
  while (q1.length + q2.length > 1) {
    var n1 = shiftMin();
    var n2 = shiftMin();
    var n = { w: n1.w + n2.w, l: n1, r: n2 };
    q2.push(n);
  }
  var root = (q1.length > 0) ? q1[0] : q2[0];
  var codeLengths = [];
  function traverseDFS(n, d) {
    if (n.l) traverseDFS(n.l, d + 1);
    if (n.r) traverseDFS(n.r, d + 1);
    if ('i' in n) codeLengths[n.i] = d;
  }
  traverseDFS(root, 1);
  return codeLengths;
}

function codeLengthsToCanonicalEncoding(codeLengths) {
  var nodes =
    codeLengths
    .map(function(l, i) { return { l: l, i: i }});
  nodes.sort(function(a, b) {
    if (a.l < b.l) return -1;
    if (a.l > b.l) return +1;
    if (a.i < b.i) return -1;
    if (a.i > b.i) return +1;
    return 0;
  });
  if (nodes.length > 0) {
    nodes[0].code = 0;
    for (var i = 1; i < nodes.length; ++i) {
      nodes[i].code = (nodes[i-1].code + 1) << (nodes[i].l - nodes[i - 1].l);
    }
  }
  var codes = [];
  for (var i = 0; i < nodes.length; ++i) {
    var j = nodes[i].i;
    delete nodes[i].i;
    codes[j] = nodes[i];
  }
  return codes;
}

function encodeASCII(str, codebook) {
  var a = [];
  var aByte = 0;
  var aByteBitLength = 0;
  function appendCode(code, l) {
    for (var i = 0; i < l; ++i) {
      var bit = (code & (1 << (l - i - 1))) ? 1 : 0;
      aByte = (aByte << 1) | bit;
      ++aByteBitLength;
      if (aByteBitLength == 8) {
        a.push(aByte);
        aByte = 0;
        aByteBitLength = 0;
      }
    }
  }
  function finishCoding() {
    var eofCode = codebook[256];
    appendCode(eofCode.code, eofCode.l);
    if (aByteBitLength > 0) {
      aByte <<= (8 - aByteBitLength);
      a.push(aByte);
    }
  }
  for (var i = 0; i < str.length; ++i) {
    var ch = str.charCodeAt(i);
    var code = codebook[ch];
    appendCode(code.code, code.l);
  }
  finishCoding();
  return a;
}

function decodeASCII(a, start, inverseCodebook) {
  var code = 0;
  var codeLength = 0;
  var str = '';
  var i = start * 8;
  for (; i < a.length * 8; ++i) {
    var byteIndex = (i / 8) >>> 0;
    var b = (typeof(a) == 'string') ? a.charCodeAt(byteIndex) : a[byteIndex];
    var bit = (b & (1 << (7 - (i % 8)))) ? 1 : 0;
    code = (code << 1) | bit;
    ++codeLength;
    if (code >= inverseCodebook.length) {
      throw new Error('Invalid code ' + code);
    }
    if (code in inverseCodebook && inverseCodebook[code].l == codeLength) {
      var ch = inverseCodebook[code].i;
      code = 0;
      codeLength = 0;
      if (ch == 256) {
        break;
      }
      str += String.fromCharCode(ch);
    }
  }
  return { end: (((i / 8) >>> 0) + 1), str: str };
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const assert = intern.getPlugin('chai').assert;
const config = require('../../../server/lib/configuration');
const crypto = require('crypto');
const css = require('css');
const extend = require('extend');
const got = require('got');
const htmlparser2 = require('htmlparser2');
const path = require('path');
const url = require('url');

// keep a cache of checked URLs to avoid duplicate tests and
// speed up the tests.
var checkedUrlPromises = {};

var httpUrl, httpsUrl = intern._config.fxaContentRoot.replace(/\/$/, '');
if (intern._config.fxaProduction) {
  assert.equal(0, httpsUrl.indexOf('https://'), 'uses https scheme');
  httpUrl = httpsUrl.replace('https://', 'http://');
} else {
  httpUrl = httpsUrl.replace(config.get('port'), config.get('http_port'));
}

function computeSri(algorithm, data) {
  if ([ 'sha256', 'sha384', 'sha512' ].indexOf(algorithm) === -1) {
    throw new Error('Unsupported hash algorithm ' + algorithm);
  }

  const sri = crypto.createHash(algorithm)
    .update(data, 'utf8')
    .digest('base64');

  return algorithm + '-' + sri;
}

function isProductionLike() {
  return intern._config.fxaProduction && intern._config.fxaDevBox;
}

function makeRequest(url, requestOptions) {
  return got(url, requestOptions)
    .catch(function (err) {
      return err.response;
    });
}

function checkHeaders(routes, route, res) {
  var headers = res.headers;

  if (headers['content-type'].indexOf('text/html') > -1) {
    // all HTML pages by default have x-frame-options: DENY
    assert.equal(headers['x-frame-options'], 'DENY');
    assert.equal(headers['x-robots-tag'], 'noindex,nofollow');

    if (routes[route].csp !== false) {
      assert.ok(headers.hasOwnProperty('content-security-policy'));
    }
  }

  assert.equal(headers['x-content-type-options'], 'nosniff');
  assert.include(headers['strict-transport-security'], 'max-age=');
}

/**
 * Go through each of the HTML files, look for URLs, check that
 * each URL exists, responds with a 200, and in the case of JS, CSS
 * and fonts, that the correct CORS headers are set.
 */
function extractAndCheckUrls(res, testName) {
  var href = url.parse(res.url);
  var origin = [ href.protocol, '//', href.host ].join('');
  return extractUrls(res.body)
    .then((resources) => checkUrls(origin, resources, testName));
}

function extractUrls(body) {
  return new Promise(function (resolve, reject) {
    var dependencyResources = [];
    var comments = [];
    var parser;

    var handlers = {
      onopentag: function onopentag(tagname, attribs) {
        if ([ 'script', 'link', 'a' ].indexOf(tagname) === -1) {
          return;
        }

        const resource = {
          integrity: attribs.integrity || null
        };

        if (tagname === 'script') {
          resource.url = attribs.src;
        } else if (tagname === 'link' || tagname === 'a') {
          resource.url = attribs.href;
        }

        if (! resource || ! resource.url) {
          return;
        }

        if (! isAbsoluteUrl(resource.url)) {
          resource.url = httpsUrl + resource.url;
        }

        dependencyResources.push(resource);
      },

      oncomment: function oncomment(text) {
        comments.push(text);
      },

      onend: function onend() {
        // TODO: could scan the accumulated comments for IE-style resource links.
        resolve(dependencyResources);
      }
    };

    const parserOptions = {
      lowerCaseAttributeNames: true,
      lowerCaseTags: true
    };

    parser = new htmlparser2.Parser(handlers, parserOptions);

    parser.write(body);
    parser.end();
  });
}

const IGNORE_URL_REGEXPS = [
  // Do not check support.mozilla.org URLs. Issue #4712
  // In February 2017 SUMO links started returning 404s to non-browser redirect requests
  /support\.mozilla\.org/,
  // skip the livereload link in the mocha tests
  /localhost:35729/,
  // Ignore firefox-source-docs URLs, two URLs in the Firefox Privacy Policy are wrong.
  // See #6330. Remove this section once https://github.com/mozilla/legal-docs/pull/1135 merges.
  /firefox-source-docs\.mozilla\.org/
];

function isUrlIgnored (url) {
  return IGNORE_URL_REGEXPS.find(domainRegExp => domainRegExp.test(url));
}

function checkUrls(origin, resources, testName = '') {
  findCssSubResources(origin, resources)
    .then((cssSubResources) => {
      resources = resources.concat(cssSubResources);

      var requests = resources.map(function (resource) {
        if (checkedUrlPromises[resource.url]) {
          return checkedUrlPromises[resource.url];
        }

        var requestOptions = {};
        if (doesURLRequireCORS(resource.url)) {
          requestOptions = {
            headers: {
              'Origin': origin
            }
          };
        }

        var promise = makeRequest(resource.url, requestOptions)
          .then(function (res) {
            if (isUrlIgnored(resource.url)) {
              return;
            }

            assert.equal(res.statusCode, 200, `${testName}: ${resource.url}`);

            // If prod-like, Check all CSS and JS (except experiments.bundle.js)
            // for SRI `integrity=` attribute on the link, and that the checksum
            // of the returned content matches.
            if (isProductionLike() && isJsOrCss(resource.url) &&
                ! /experiments\.bundle\.js$/.test(resource.url)) {
              assert.ok(resource.integrity, 'JS/CSS link has SRI integrity attribute');
              var algorithm = resource.integrity.split('-')[0];
              var computedSri = computeSri(algorithm, res.body);
              assert.equal(resource.integrity, computedSri, 'SRI attribute integrity matches downloaded content');
            }

            var hasCORSHeaders = res.headers.hasOwnProperty('access-control-allow-origin');
            if (doesURLRequireCORS(resource.url)) {
              assert.ok(hasCORSHeaders, resource.url + ' should have CORS headers');
            } else {
              assert.notOk(hasCORSHeaders, resource.url + ' should not have CORS headers');
            }
          });

        checkedUrlPromises[resource.url] = promise;

        return promise;
      });

      return Promise.all(requests);
    });
}

function findCssUrlMatches(value, base) {
  const CSSURL_RE = /url\(\s*['"]?([^)'"]+)['"]?\s*\)/g;

  var urls = {};
  var match;

  while (match = CSSURL_RE.exec(value)) { // eslint-disable-line no-cond-assign
    var resolved = url.resolve(base, match[1]);
    var protocol = url.parse(resolved).protocol;
    if (protocol.match(/^http/)) {
      urls[resolved] = 1;
    }
  }

  return urls;
}

function parseCssUrls(content, base) {
  var ast = css.parse(content);
  var urls = {};

  ast.stylesheet.rules.forEach(function(rule) {
    if (rule.type === 'font-face' || rule.type === 'rule') {
      rule.declarations.forEach(function(declaration) {
        extend(urls, findCssUrlMatches(declaration.value, base));
      });
    } else if (rule.type === 'media') {
      rule.rules.forEach(function(mediaRule) {
        mediaRule.declarations.forEach(function(declaration) {
          extend(urls, findCssUrlMatches(declaration.value, base));
        });
      });
    }
  });

  return Object.keys(urls).sort();
}

function filterCssUrls(resources) {
  return resources.filter(function (resource) {
    var parsedUri = url.parse(resource.url);
    var extension = path.extname(parsedUri.pathname);
    if (extension === '.css') {
      return resource.url;
    }
  });
}

function findCssSubResources(origin, resources) {
  // For urls with extension `.css`, find the font and image resources
  // entrained from CSS.
  var cssResources = filterCssUrls(resources);

  var requests = cssResources.map(function (resource) {
    if (checkedUrlPromises[resource.url]) {
      return checkedUrlPromises[resource.url];
    }

    var requestOptions = {
      headers: {
        Accept: 'text/css,*/*;q=0.1'
      }
    };

    if (doesURLRequireCORS(resource.url)) {
      requestOptions.headers.Origin = origin;
    }

    var promise = makeRequest(resource.url, requestOptions)
      .then(function (res) {
        // Only a minimal check here. The resolved Promise response will be
        // checked in detail again in `checkUrls()`.
        assert.equal(res.statusCode, 200, resource.url);

        return parseCssUrls(res.body, resource.url);
      });

    checkedUrlPromises[resource.url] = promise;

    return promise;
  });

  return Promise.all(requests)
    .then(function (resources) {
      // convert the return value to a flattened list of resource objects
      var flattened = [].concat.apply([], resources);
      return flattened.map((url) => {
        return {
          url: url
        };
      });
    });
}

function isAbsoluteUrl(url) {
  return /^http/.test(url);
}

function doesURLRequireCORS(url) {
  return isExternalUrl(url) && doesExtensionRequireCORS(url);
}

function isContentServerUrl(url) {
  return url.indexOf(httpsUrl) === 0 ||
         url.indexOf(httpUrl) === 0;
}

function isExternalUrl(url) {
  return ! isContentServerUrl(url);
}

function doesExtensionRequireCORS(uri) {
  var parsedUri = url.parse(uri);
  var extension = path.extname(parsedUri.pathname);
  return /^\.(js|css|woff|woff2|eot|ttf)$/.test(extension);
}

function isJs(uri) {
  var parsedUri = url.parse(uri);
  var extension = path.extname(parsedUri.pathname);
  return extension === '.js';
}

function isCss(uri) {
  var parsedUri = url.parse(uri);
  var extension = path.extname(parsedUri.pathname);
  return extension === '.css';
}

function isJsOrCss(url) {
  return isJs(url) || isCss(url);
}

module.exports = {
  checkHeaders: checkHeaders,
  extractAndCheckUrls: extractAndCheckUrls,
  makeRequest: makeRequest
};
<commit_msg>chore(test): Re-enable link checks in Fx TOS/PP (#6412), r=@philbooth<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const assert = intern.getPlugin('chai').assert;
const config = require('../../../server/lib/configuration');
const crypto = require('crypto');
const css = require('css');
const extend = require('extend');
const got = require('got');
const htmlparser2 = require('htmlparser2');
const path = require('path');
const url = require('url');

// keep a cache of checked URLs to avoid duplicate tests and
// speed up the tests.
var checkedUrlPromises = {};

var httpUrl, httpsUrl = intern._config.fxaContentRoot.replace(/\/$/, '');
if (intern._config.fxaProduction) {
  assert.equal(0, httpsUrl.indexOf('https://'), 'uses https scheme');
  httpUrl = httpsUrl.replace('https://', 'http://');
} else {
  httpUrl = httpsUrl.replace(config.get('port'), config.get('http_port'));
}

function computeSri(algorithm, data) {
  if ([ 'sha256', 'sha384', 'sha512' ].indexOf(algorithm) === -1) {
    throw new Error('Unsupported hash algorithm ' + algorithm);
  }

  const sri = crypto.createHash(algorithm)
    .update(data, 'utf8')
    .digest('base64');

  return algorithm + '-' + sri;
}

function isProductionLike() {
  return intern._config.fxaProduction && intern._config.fxaDevBox;
}

function makeRequest(url, requestOptions) {
  return got(url, requestOptions)
    .catch(function (err) {
      return err.response;
    });
}

function checkHeaders(routes, route, res) {
  var headers = res.headers;

  if (headers['content-type'].indexOf('text/html') > -1) {
    // all HTML pages by default have x-frame-options: DENY
    assert.equal(headers['x-frame-options'], 'DENY');
    assert.equal(headers['x-robots-tag'], 'noindex,nofollow');

    if (routes[route].csp !== false) {
      assert.ok(headers.hasOwnProperty('content-security-policy'));
    }
  }

  assert.equal(headers['x-content-type-options'], 'nosniff');
  assert.include(headers['strict-transport-security'], 'max-age=');
}

/**
 * Go through each of the HTML files, look for URLs, check that
 * each URL exists, responds with a 200, and in the case of JS, CSS
 * and fonts, that the correct CORS headers are set.
 */
function extractAndCheckUrls(res, testName) {
  var href = url.parse(res.url);
  var origin = [ href.protocol, '//', href.host ].join('');
  return extractUrls(res.body)
    .then((resources) => checkUrls(origin, resources, testName));
}

function extractUrls(body) {
  return new Promise(function (resolve, reject) {
    var dependencyResources = [];
    var comments = [];
    var parser;

    var handlers = {
      onopentag: function onopentag(tagname, attribs) {
        if ([ 'script', 'link', 'a' ].indexOf(tagname) === -1) {
          return;
        }

        const resource = {
          integrity: attribs.integrity || null
        };

        if (tagname === 'script') {
          resource.url = attribs.src;
        } else if (tagname === 'link' || tagname === 'a') {
          resource.url = attribs.href;
        }

        if (! resource || ! resource.url) {
          return;
        }

        if (! isAbsoluteUrl(resource.url)) {
          resource.url = httpsUrl + resource.url;
        }

        dependencyResources.push(resource);
      },

      oncomment: function oncomment(text) {
        comments.push(text);
      },

      onend: function onend() {
        // TODO: could scan the accumulated comments for IE-style resource links.
        resolve(dependencyResources);
      }
    };

    const parserOptions = {
      lowerCaseAttributeNames: true,
      lowerCaseTags: true
    };

    parser = new htmlparser2.Parser(handlers, parserOptions);

    parser.write(body);
    parser.end();
  });
}

const IGNORE_URL_REGEXPS = [
  // Do not check support.mozilla.org URLs. Issue #4712
  // In February 2017 SUMO links started returning 404s to non-browser redirect requests
  /support\.mozilla\.org/,
  // skip the livereload link in the mocha tests
  /localhost:35729/
];

function isUrlIgnored (url) {
  return IGNORE_URL_REGEXPS.find(domainRegExp => domainRegExp.test(url));
}

function checkUrls(origin, resources, testName = '') {
  findCssSubResources(origin, resources)
    .then((cssSubResources) => {
      resources = resources.concat(cssSubResources);

      var requests = resources.map(function (resource) {
        if (checkedUrlPromises[resource.url]) {
          return checkedUrlPromises[resource.url];
        }

        var requestOptions = {};
        if (doesURLRequireCORS(resource.url)) {
          requestOptions = {
            headers: {
              'Origin': origin
            }
          };
        }

        var promise = makeRequest(resource.url, requestOptions)
          .then(function (res) {
            if (isUrlIgnored(resource.url)) {
              return;
            }

            assert.equal(res.statusCode, 200, `${testName}: ${resource.url}`);

            // If prod-like, Check all CSS and JS (except experiments.bundle.js)
            // for SRI `integrity=` attribute on the link, and that the checksum
            // of the returned content matches.
            if (isProductionLike() && isJsOrCss(resource.url) &&
                ! /experiments\.bundle\.js$/.test(resource.url)) {
              assert.ok(resource.integrity, 'JS/CSS link has SRI integrity attribute');
              var algorithm = resource.integrity.split('-')[0];
              var computedSri = computeSri(algorithm, res.body);
              assert.equal(resource.integrity, computedSri, 'SRI attribute integrity matches downloaded content');
            }

            var hasCORSHeaders = res.headers.hasOwnProperty('access-control-allow-origin');
            if (doesURLRequireCORS(resource.url)) {
              assert.ok(hasCORSHeaders, resource.url + ' should have CORS headers');
            } else {
              assert.notOk(hasCORSHeaders, resource.url + ' should not have CORS headers');
            }
          });

        checkedUrlPromises[resource.url] = promise;

        return promise;
      });

      return Promise.all(requests);
    });
}

function findCssUrlMatches(value, base) {
  const CSSURL_RE = /url\(\s*['"]?([^)'"]+)['"]?\s*\)/g;

  var urls = {};
  var match;

  while (match = CSSURL_RE.exec(value)) { // eslint-disable-line no-cond-assign
    var resolved = url.resolve(base, match[1]);
    var protocol = url.parse(resolved).protocol;
    if (protocol.match(/^http/)) {
      urls[resolved] = 1;
    }
  }

  return urls;
}

function parseCssUrls(content, base) {
  var ast = css.parse(content);
  var urls = {};

  ast.stylesheet.rules.forEach(function(rule) {
    if (rule.type === 'font-face' || rule.type === 'rule') {
      rule.declarations.forEach(function(declaration) {
        extend(urls, findCssUrlMatches(declaration.value, base));
      });
    } else if (rule.type === 'media') {
      rule.rules.forEach(function(mediaRule) {
        mediaRule.declarations.forEach(function(declaration) {
          extend(urls, findCssUrlMatches(declaration.value, base));
        });
      });
    }
  });

  return Object.keys(urls).sort();
}

function filterCssUrls(resources) {
  return resources.filter(function (resource) {
    var parsedUri = url.parse(resource.url);
    var extension = path.extname(parsedUri.pathname);
    if (extension === '.css') {
      return resource.url;
    }
  });
}

function findCssSubResources(origin, resources) {
  // For urls with extension `.css`, find the font and image resources
  // entrained from CSS.
  var cssResources = filterCssUrls(resources);

  var requests = cssResources.map(function (resource) {
    if (checkedUrlPromises[resource.url]) {
      return checkedUrlPromises[resource.url];
    }

    var requestOptions = {
      headers: {
        Accept: 'text/css,*/*;q=0.1'
      }
    };

    if (doesURLRequireCORS(resource.url)) {
      requestOptions.headers.Origin = origin;
    }

    var promise = makeRequest(resource.url, requestOptions)
      .then(function (res) {
        // Only a minimal check here. The resolved Promise response will be
        // checked in detail again in `checkUrls()`.
        assert.equal(res.statusCode, 200, resource.url);

        return parseCssUrls(res.body, resource.url);
      });

    checkedUrlPromises[resource.url] = promise;

    return promise;
  });

  return Promise.all(requests)
    .then(function (resources) {
      // convert the return value to a flattened list of resource objects
      var flattened = [].concat.apply([], resources);
      return flattened.map((url) => {
        return {
          url: url
        };
      });
    });
}

function isAbsoluteUrl(url) {
  return /^http/.test(url);
}

function doesURLRequireCORS(url) {
  return isExternalUrl(url) && doesExtensionRequireCORS(url);
}

function isContentServerUrl(url) {
  return url.indexOf(httpsUrl) === 0 ||
         url.indexOf(httpUrl) === 0;
}

function isExternalUrl(url) {
  return ! isContentServerUrl(url);
}

function doesExtensionRequireCORS(uri) {
  var parsedUri = url.parse(uri);
  var extension = path.extname(parsedUri.pathname);
  return /^\.(js|css|woff|woff2|eot|ttf)$/.test(extension);
}

function isJs(uri) {
  var parsedUri = url.parse(uri);
  var extension = path.extname(parsedUri.pathname);
  return extension === '.js';
}

function isCss(uri) {
  var parsedUri = url.parse(uri);
  var extension = path.extname(parsedUri.pathname);
  return extension === '.css';
}

function isJsOrCss(url) {
  return isJs(url) || isCss(url);
}

module.exports = {
  checkHeaders: checkHeaders,
  extractAndCheckUrls: extractAndCheckUrls,
  makeRequest: makeRequest
};
<|endoftext|>


<commit_before>import { test, moduleForComponent } from 'ember-qunit';
import startApp from '../../helpers/start-app';
import Ember from 'ember';
import JWT from 'simple-auth-token/authenticators/jwt';
import Configuration from 'simple-auth-token/configuration';

var App;

module('JWT Authenticator', {
  setup: function() {
    App = startApp();
    App.xhr = sinon.useFakeXMLHttpRequest();
    App.server = sinon.fakeServer.create();
    App.server.autoRespond = true;
    App.authenticator = JWT.create();
  },
  teardown: function() {
    Ember.run(App, App.destroy);
    App.xhr.restore();
  }
});

test('assigns serverTokenRefreshEndpoint from the configuration object', function() {
  Configuration.serverTokenRefreshEndpoint = 'serverTokenRefreshEndpoint';

  equal(JWT.create().serverTokenRefreshEndpoint, 'serverTokenRefreshEndpoint');

  Configuration.load({}, {});
});

test('assigns refreshAccessTokens from the configuration object', function() {
  Configuration.refreshAccessTokens = 'refreshAccessTokens';

  equal(JWT.create().refreshAccessTokens, 'refreshAccessTokens');

  Configuration.load({}, {});
});

test('assigns tokenExpireName from the configuration object', function() {
  Configuration.tokenExpireName = 'tokenExpireName';

  equal(JWT.create().tokenExpireName, 'tokenExpireName');

  Configuration.load({}, {});
});

test('assigns timeFactor from the configuration object', function() {
  Configuration.timeFactor = 'timeFactor';

  equal(JWT.create().timeFactor, 'timeFactor');

  Configuration.load({}, {});
});

test('#restore resolves when the data includes `token` and `expiresAt`', function() {
  var jwt = JWT.create(),
    expiresAt = (new Date()).getTime() - 60000;

  var token = {};
  token[jwt.identificationField] = 'test@test.com';
  token[jwt.tokenExpireName] = expiresAt;

  token = window.btoa(JSON.stringify(token));

  var data = {};
  data[jwt.tokenPropertyName] = token;
  data[jwt.tokenExpireName] = expiresAt;

  App.server.respondWith('POST', '/api-token-refresh/', [
    201,
    {
      'Content-Type': 'application/json'
    },
    '{ "token": "' + token + '"}'
  ]);

  Ember.run(function(){
    App.authenticator.restore(data).then(function(content){
      deepEqual(content, data);
    });
  });
});

test('#restore resolves when the data includes `token` and excludes `expiresAt`', function() {
  var jwt = JWT.create(),
    expiresAt = (new Date()).getTime() - 60000;

  var token = {};
  token[jwt.identificationField] = 'test@test.com';
  token[jwt.tokenExpireName] = expiresAt;

  token = window.btoa(JSON.stringify(token));

  var data = {};
  data[jwt.tokenPropertyName] = token;

  App.server.respondWith('POST', '/api-token-refresh/', [
    201,
    {
      'Content-Type': 'application/json'
    },
    '{ "token": "' + token + '"}'
  ]);

  Ember.run(function(){
    App.authenticator.restore(data).then(function(content){
      deepEqual(content, data);
    });
  });
});

test('#restore rejects when `refreshAccessTokens` is false', function() {
  var jwt = JWT.create(),
    expiresAt = (new Date()).getTime() - 60000;

  var token = {};
  token[jwt.identificationField] = 'test@test.com';
  token[jwt.tokenExpireName] = expiresAt;

  token = window.btoa(JSON.stringify(token));

  var data = {};
  data[jwt.tokenPropertyName] = token;
  data['expiresAt'] = expiresAt;

  App.authenticator.refreshAccessTokens = false;

  App.server.respondWith('POST', '/api-token-refresh/', [
    201,
    {
      'Content-Type': 'application/json'
    },
    '{ "token": "' + token + '"}'
  ]);

  Ember.run(function(){
    App.authenticator.restore(data).then(null, function(){
      ok(true, 'If we are there then the promise was rejected.');
    });
  });
});

test('#restore rejects when `token` is excluded.', function() {
  var jwt = JWT.create(),
    expiresAt = (new Date()).getTime() - 60000;

  var token = {};
  token[jwt.identificationField] = 'test@test.com';
  token[jwt.tokenExpireName] = expiresAt;

  token = window.btoa(JSON.stringify(token));

  var data = {};
  data['expiresAt'] = expiresAt;

  App.authenticator.refreshAccessTokens = false;

  App.server.respondWith('POST', '/api-token-refresh/', [
    201,
    {
      'Content-Type': 'application/json'
    },
    '{ "token": "' + token + '"}'
  ]);

  Ember.run(function(){
    App.authenticator.restore(data).then(null, function(){
      ok(true, 'If we are there then the promise was rejected.');
    });
  });
});

test('#restore resolves when `expiresAt` is greater than `now`', function() {
  var jwt = JWT.create(),
    expiresAt = (new Date()).getTime() + 60000;

  var token = {};
  token[jwt.identificationField] = 'test@test.com';
  token[jwt.tokenExpireName] = expiresAt;

  token = window.btoa(JSON.stringify(token));

  var data = {};
  data[jwt.tokenPropertyName] = token;
  data[jwt.tokenExpireName] = expiresAt;

  App.authenticator.refreshAccessTokens = false;

  App.server.respondWith('POST', '/api-token-refresh/', [
    201,
    {
      'Content-Type': 'application/json'
    },
    '{ "token": "' + token + '"}'
  ]);

  Ember.run(function(){
    App.authenticator.restore(data).then(function(content){
      deepEqual(content, data);
    });
  });
});

test('#restore schedules a token refresh when `refreshAccessTokens` is true.', function() {
  sinon.spy(Ember.run, 'later');

  var jwt = JWT.create(),
    expiresAt = (new Date()).getTime() + 60000;

  var token = {};
  token[jwt.identificationField] = 'test@test.com';
  token[jwt.tokenExpireName] = expiresAt;

  token = window.btoa(JSON.stringify(token));

  var data = {};
  data[jwt.tokenPropertyName] = token;
  data[jwt.tokenExpireName] = expiresAt;

  Ember.run(function(){
    App.authenticator.restore(data).then(function(content){
      var spyCall = Ember.run.later.getCall(0);

      deepEqual(spyCall.args[1], App.authenticator.refreshAccessToken);
      deepEqual(spyCall.args[2], data.token);
    });
  });

  Ember.run.later.restore();
});

test('#restore does not schedule a token refresh when `refreshAccessTokens` is false.', function() {
  sinon.spy(Ember.run, 'later');

  var jwt = JWT.create(),
    expiresAt = (new Date()).getTime() + 60000;

  var token = {};
  token[jwt.identificationField] = 'test@test.com';
  token[jwt.tokenExpireName] = expiresAt;

  token = window.btoa(JSON.stringify(token));

  var data = {};
  data[jwt.tokenPropertyName] = token;
  data[jwt.tokenExpireName] = expiresAt;

  App.authenticator.refreshAccessTokens = false;

  Ember.run(function(){
    App.authenticator.restore(data).then(function(content){
      var spyCall = Ember.run.later.getCall(0);

      deepEqual(spyCall, null);
    });
  });

  Ember.run.later.restore();
});
<commit_msg>Added temp fix for Ember.run.later<commit_after>import { test, moduleForComponent } from 'ember-qunit';
import startApp from '../../helpers/start-app';
import Ember from 'ember';
import JWT from 'simple-auth-token/authenticators/jwt';
import Configuration from 'simple-auth-token/configuration';

var App;

module('JWT Authenticator', {
  setup: function() {
    App = startApp();
    App.xhr = sinon.useFakeXMLHttpRequest();
    App.server = sinon.fakeServer.create();
    App.server.autoRespond = true;
    App.authenticator = JWT.create();
  },
  teardown: function() {
    Ember.run(App, App.destroy);
    App.xhr.restore();
  }
});

test('assigns serverTokenRefreshEndpoint from the configuration object', function() {
  Configuration.serverTokenRefreshEndpoint = 'serverTokenRefreshEndpoint';

  equal(JWT.create().serverTokenRefreshEndpoint, 'serverTokenRefreshEndpoint');

  Configuration.load({}, {});
});

test('assigns refreshAccessTokens from the configuration object', function() {
  Configuration.refreshAccessTokens = 'refreshAccessTokens';

  equal(JWT.create().refreshAccessTokens, 'refreshAccessTokens');

  Configuration.load({}, {});
});

test('assigns tokenExpireName from the configuration object', function() {
  Configuration.tokenExpireName = 'tokenExpireName';

  equal(JWT.create().tokenExpireName, 'tokenExpireName');

  Configuration.load({}, {});
});

test('assigns timeFactor from the configuration object', function() {
  Configuration.timeFactor = 'timeFactor';

  equal(JWT.create().timeFactor, 'timeFactor');

  Configuration.load({}, {});
});

test('#restore resolves when the data includes `token` and `expiresAt`', function() {
  var jwt = JWT.create(),
    expiresAt = (new Date()).getTime() - 60000;

  var token = {};
  token[jwt.identificationField] = 'test@test.com';
  token[jwt.tokenExpireName] = expiresAt;

  token = window.btoa(JSON.stringify(token));

  var data = {};
  data[jwt.tokenPropertyName] = token;
  data[jwt.tokenExpireName] = expiresAt;

  App.server.respondWith('POST', '/api-token-refresh/', [
    201,
    {
      'Content-Type': 'application/json'
    },
    '{ "token": "' + token + '"}'
  ]);

  Ember.run(function(){
    App.authenticator.restore(data).then(function(content){
      deepEqual(content, data);
    });
  });
});

test('#restore resolves when the data includes `token` and excludes `expiresAt`', function() {
  var jwt = JWT.create(),
    expiresAt = (new Date()).getTime() - 60000;

  var token = {};
  token[jwt.identificationField] = 'test@test.com';
  token[jwt.tokenExpireName] = expiresAt;

  token = window.btoa(JSON.stringify(token));

  var data = {};
  data[jwt.tokenPropertyName] = token;

  App.server.respondWith('POST', '/api-token-refresh/', [
    201,
    {
      'Content-Type': 'application/json'
    },
    '{ "token": "' + token + '"}'
  ]);

  Ember.run(function(){
    App.authenticator.restore(data).then(function(content){
      deepEqual(content, data);
    });
  });
});

test('#restore rejects when `refreshAccessTokens` is false', function() {
  var jwt = JWT.create(),
    expiresAt = (new Date()).getTime() - 60000;

  var token = {};
  token[jwt.identificationField] = 'test@test.com';
  token[jwt.tokenExpireName] = expiresAt;

  token = window.btoa(JSON.stringify(token));

  var data = {};
  data[jwt.tokenPropertyName] = token;
  data['expiresAt'] = expiresAt;

  App.authenticator.refreshAccessTokens = false;

  App.server.respondWith('POST', '/api-token-refresh/', [
    201,
    {
      'Content-Type': 'application/json'
    },
    '{ "token": "' + token + '"}'
  ]);

  Ember.run(function(){
    App.authenticator.restore(data).then(null, function(){
      ok(true, 'If we are there then the promise was rejected.');
    });
  });
});

test('#restore rejects when `token` is excluded.', function() {
  var jwt = JWT.create(),
    expiresAt = (new Date()).getTime() - 60000;

  var token = {};
  token[jwt.identificationField] = 'test@test.com';
  token[jwt.tokenExpireName] = expiresAt;

  token = window.btoa(JSON.stringify(token));

  var data = {};
  data['expiresAt'] = expiresAt;

  App.authenticator.refreshAccessTokens = false;

  App.server.respondWith('POST', '/api-token-refresh/', [
    201,
    {
      'Content-Type': 'application/json'
    },
    '{ "token": "' + token + '"}'
  ]);

  Ember.run(function(){
    App.authenticator.restore(data).then(null, function(){
      ok(true, 'If we are there then the promise was rejected.');
    });
  });
});

test('#restore resolves when `expiresAt` is greater than `now`', function() {
  var jwt = JWT.create(),
    expiresAt = (new Date()).getTime() + 60000;

  var token = {};
  token[jwt.identificationField] = 'test@test.com';
  token[jwt.tokenExpireName] = expiresAt;

  token = window.btoa(JSON.stringify(token));

  var data = {};
  data[jwt.tokenPropertyName] = token;
  data[jwt.tokenExpireName] = expiresAt;

  App.authenticator.refreshAccessTokens = false;

  App.server.respondWith('POST', '/api-token-refresh/', [
    201,
    {
      'Content-Type': 'application/json'
    },
    '{ "token": "' + token + '"}'
  ]);

  Ember.run(function(){
    App.authenticator.restore(data).then(function(content){
      deepEqual(content, data);
    });
  });
});

test('#restore schedules a token refresh when `refreshAccessTokens` is true.', function() {
  sinon.spy(Ember.run, 'later');

  var jwt = JWT.create(),
    expiresAt = (new Date()).getTime() + 60000;

  var token = {};
  token[jwt.identificationField] = 'test@test.com';
  token[jwt.tokenExpireName] = expiresAt;

  token = window.btoa(JSON.stringify(token));

  var data = {};
  data[jwt.tokenPropertyName] = token;
  data[jwt.tokenExpireName] = expiresAt;

  // This is bad - waiting for better solution, required to check Ember.run.later.
  Ember.testing = false;

  Ember.run(function(){
    App.authenticator.restore(data).then(function(content){
      var spyCall = Ember.run.later.getCall(0);

      deepEqual(spyCall.args[1], App.authenticator.refreshAccessToken);
      deepEqual(spyCall.args[2], data.token);
    });
  });

  Ember.testing = true;

  Ember.run.later.restore();
});

test('#restore does not schedule a token refresh when `refreshAccessTokens` is false.', function() {
  sinon.spy(Ember.run, 'later');

  var jwt = JWT.create(),
    expiresAt = (new Date()).getTime() + 60000;

  var token = {};
  token[jwt.identificationField] = 'test@test.com';
  token[jwt.tokenExpireName] = expiresAt;

  token = window.btoa(JSON.stringify(token));

  var data = {};
  data[jwt.tokenPropertyName] = token;
  data[jwt.tokenExpireName] = expiresAt;

  App.authenticator.refreshAccessTokens = false;

  Ember.run(function(){
    App.authenticator.restore(data).then(function(content){
      var spyCall = Ember.run.later.getCall(0);

      deepEqual(spyCall, null);
    });
  });

  Ember.run.later.restore();
});
<|endoftext|>


<commit_before>'use strict';

var enc = new TextEncoder();

exports.stringToBuffer = function(str) {
  return enc.encode(str);
};

exports.stringToSocketData = function(str) {
  return enc.encode(str);
};

/* global runtime */
var TCPServerSocket = runtime.net.TCPServerSocket;

exports.createServerHandle = function(httpServer) {
  var socket = new TCPServerSocket();
  socket.onconnect = function(connSocket) {
    connSocket.ondata = function(u8) {
      httpServer._dataHandler(connSocket, u8);
    };

    connSocket.onend = function() {
      httpServer._endHandler(connSocket);
    };

    httpServer._connectionHandler(connSocket);
  };

  return socket;
};

exports.listen = function(handle, port) {
  handle.listen(port);
};

exports.unlisten = function(handle) {
  handle.close();
};

exports.sendAndClose = function(socket, u8) {
  socket.send(u8);
  socket.close();
};

exports.close = function(socket) {
  socket.close();
};

exports.send = function(socket, u8) {
  socket.send(u8);
};
<commit_msg>Add runtime.js client handler<commit_after>'use strict';

/* global runtime */
var TCPServerSocket = runtime.net.TCPServerSocket;
var TCPSocket = runtime.net.TCPSocket;
var enc = new TextEncoder();

exports.stringToBuffer = function(str) {
  return enc.encode(str);
};

exports.stringToSocketData = function(str) {
  return enc.encode(str);
};

exports.createServerHandle = function(httpServer) {
  var socket = new TCPServerSocket();
  socket.onconnect = function(connSocket) {
    connSocket.ondata = function(u8) {
      httpServer._dataHandler(connSocket, u8);
    };

    connSocket.onend = function() {
      httpServer._endHandler(connSocket);
    };

    httpServer._connectionHandler(connSocket);
  };

  return socket;
};

exports.listen = function(handle, port) {
  handle.listen(port);
};

exports.unlisten = function(handle) {
  handle.close();
};

exports.sendAndClose = function(socket, u8) {
  socket.send(u8);
  socket.close();
};

exports.close = function(socket) {
  socket.close();
};

exports.send = function(socket, u8) {
  socket.send(u8);
};

exports.createClientHandle = function(httpClient) {
  var socket = new TCPSocket();

  socket.onopen = function() {
    httpClient._openHandler();
  }

  socket.ondata = function(u8) {
    httpClient._dataHandler(u8);
  }

  socket.onend = function() {
    socket.close();
    httpClient._endHandler();
  }

  socket.onclose = function() {
    httpClient._closeHandler();
  }

  return socket;
}

exports.closeClientHandle = function(socket) {
  socket.close();
}

exports.connect = function(handle, ip, port) {
  handle.open(ip, port);
}
<|endoftext|>


<commit_before>var mongoose = require('mongoose');
var validator = require('validator');
var utils = require('../lib/utils');
var renderInitial = require('../lib/mes-aides/emails/initial').render;

var FollowupSchema = new mongoose.Schema({
    situation: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Situation'
    },
    email: {
        type: String,
        validate:{
            validator: validator.isEmail,
            message: '{VALUE} n\'est pas un email valide',
            isAsync: false
        }
    },
    createdAt: { type: Date, default: Date.now },
    sentAt: { type: Date, default: Date.now },
    _id: { type: String },
}, { minimize: false, id: false });

FollowupSchema.methods.renderInitial = function() {
    return renderInitial(this);
};
FollowupSchema.pre('save', function(next) {
    if (!this.isNew) next();
    var followup = this;
    utils.generateToken()
        .then(function(token) {
            followup._id = token;
        })
        .then(next)
        .catch(next);
});
FollowupSchema.virtual('returnPath').get(function() {
    return '/api/followups/' + this._id;
});

mongoose.model('Followup', FollowupSchema);
<commit_msg>Ne met pas une valeur par défaut à sentAt<commit_after>var mongoose = require('mongoose');
var validator = require('validator');
var utils = require('../lib/utils');
var renderInitial = require('../lib/mes-aides/emails/initial').render;

var FollowupSchema = new mongoose.Schema({
    situation: {
        type: mongoose.Schema.Types.ObjectId,
        ref: 'Situation'
    },
    email: {
        type: String,
        validate:{
            validator: validator.isEmail,
            message: '{VALUE} n\'est pas un email valide',
            isAsync: false
        }
    },
    createdAt: { type: Date, default: Date.now },
    sentAt: { type: Date },
    _id: { type: String },
}, { minimize: false, id: false });

FollowupSchema.methods.renderInitial = function() {
    return renderInitial(this);
};
FollowupSchema.pre('save', function(next) {
    if (!this.isNew) next();
    var followup = this;
    utils.generateToken()
        .then(function(token) {
            followup._id = token;
        })
        .then(next)
        .catch(next);
});
FollowupSchema.virtual('returnPath').get(function() {
    return '/api/followups/' + this._id;
});

mongoose.model('Followup', FollowupSchema);
<|endoftext|>


<commit_before>// no idea about proper javascript naming conventions
// if anyone knows, please find+replace and PR, I'll test.

var TGateway = function(config) {
  this.config = config;

  // Front/back-end drivers, ready to be initialised into the separate front and
  // back-end instances later.
  this.frontendDrivers = {};
  this.backendDrivers  = {};

  // Front/back-end instances
  this.frontends = [];
  this.backends  = [];
};

TGateway.NAME = "tgateway";
TGateway.VERSION = "0.0.1";

TGateway.prototype.loadFrontendDriver = function(typeId, driver) {
  this.frontendDrivers[typeId] = driver;
  console.log("Loaded frontend driver " + typeId);
}

TGateway.prototype.loadBackendDriver = function(typeId, driver) {
  this.backendDrivers[typeId] = driver;
  console.log("Loaded backend driver " + typeId);
}

TGateway.prototype.run = function() {
  console.log(TGateway.NAME + " version " + TGateway.VERSION + " starting...");

  // Load the frontends from the config
  console.log("initializing frontends...");
  for(var obj of this.config.frontends) {
    console.log("  " + obj.type);
    if(this.frontendDrivers[obj.type] === undefined) {
      console.log("    - skipping as driver was not loaded");
    } else {
      // Initialize the front end with the frontend config
      var frontend = new (this.frontendDrivers[obj.type])(this, obj);
      this.frontends.push(frontend);
      frontend.init();
    }
  }
}

module.exports = TGateway;
<commit_msg>load more files and basic connect handler<commit_after>// no idea about proper javascript naming conventions
// if anyone knows, please find+replace and PR, I'll test.

var Connection = require('./connection');

var TGateway = function(config) {
  this.config = config;

  // Front/back-end drivers, ready to be initialised into the separate front and
  // back-end instances later.
  this.frontendDrivers = {};
  this.backendDrivers  = {};

  // Front/back-end instances
  this.frontends = []; // TODO turn this into a hash, frontend ids
  this.backends  = {};

  // Menu templates
  this.templates = {};

  this.connections = []; // Array of connection objects
}

TGateway.NAME = "tgateway";
TGateway.VERSION = "0.0.1";

TGateway.prototype.loadFrontendDriver = function(typeId, driver) {
  this.frontendDrivers[typeId] = driver;
  console.log("Loaded frontend driver " + typeId);
}

TGateway.prototype.loadBackendDriver = function(typeId, driver) {
  this.backendDrivers[typeId] = driver;
  console.log("Loaded backend driver " + typeId);
}

TGateway.prototype.run = function() {
  console.log(TGateway.NAME + " version " + TGateway.VERSION + " starting...");

  // Using all synchronous code here for the setup
  // as we can wait until everything is loaded

  // Load menu templates
  console.log('loading menu templates...');
  for(var file of fs.readdirSync('templates')) {
    this.templates[file] = fs.readFileSync(file);
  }

  // Load the backends from the config
  console.log("initializing backends...");
  for(var id in this.config.backends) {
    var obj = this.config.backends[id];
    console.log("  " + id + " " + obj.type);
    if(this.backendDrivers[obj.type] === undefined) {
      console.log("    - skipping as driver was not loaded");
    } else {
      // TODO initialize backend
    }
  }

  // Load the frontends from the config
  console.log("initializing frontends...");
  for(var obj of this.config.frontends) {
    console.log("  " + obj.type);
    if(this.frontendDrivers[obj.type] === undefined) {
      console.log("    - skipping as driver was not loaded");
    } else {
      // Initialize the front end with the frontend config
      var frontend = new (this.frontendDrivers[obj.type])(this, obj);
      this.frontends.push(frontend);
      frontend.init();
    }
  }
}

TGateway.prototype.connectEndpoint = function(endpoint, frontend) {
  endpoint.puts(TGateway.NAME + " version " + TGateway.VERSION);

}

module.exports = TGateway;
<|endoftext|>


<commit_before>function check_insert(){
  if(confirm("추가하시겠습니까?")){
    insert_classitem($(".clicked"));
  }else{
    alert("close");
  }
}

function day_to_code(day){
  switch (day){
    case '월' : ret = 'Mon'; break;
    case '화' : ret = 'Tue'; break;
    case '수' : ret = 'Wed'; break;
    case '목' : ret = 'Thu'; break;
    case '금' : ret = 'Fri'; break;
  }
  return ret;
}

function random_color(){
  var colors = new Array("red","orange","yellow","olive","green","teal","blue",
  "violet","purple","pink","grey","black");
  return colors[Math.floor(Math.random() * colors.length)];
}

function time_parser(elem){
  var row_dict = {};
  classname = $(elem)[0].children[1].innerHTML;
  prof = $(elem)[0].children[4].innerHTML;
  classtime = $(elem)[0].children[7].innerHTML.split(","); //["월13:00-14:50 (Y5420)", "수13:00-14:50 (Y5420)"]

  daypattern = /(월|화|수|목|금)/;
  timepattern = /\d{2}:\d{2}/g;
  classroompattern = /\w\d{3,5}/g;

  days = [];
  times=[];
  classrooms=[];

  // except empty
  $.each(classtime, function(index, value){
    if(value=="미입력"){
      days.push("empty");
      times.push("empty");
      classrooms.push("empty");
    }else{
      day= value.match(daypattern);
      time = value.match(timepattern);
      classroom = value.match(classroompattern);
      days.push(day[0]); // push() method : new value to array, append()는 html에 추가할때 사용, day가 array라서 index붙임
      times.push(time);
      if(classroom == null){
        classrooms.push("empty");
      }else{
        classrooms.push(classroom[0]);
      }
    }
  });

  row_dict['classname'] = classname;
  row_dict['prof'] = prof;
  row_dict['classtime'] = classtime;
  row_dict['days'] = days;
  row_dict['times'] = times;
  row_dict['classrooms'] = classrooms;

  return row_dict;
}

function set_highlight(rowobj){
  // row의 time parsing 된 dictionary 받아 hover event 줌
  $.each(rowobj['days'], function(i,val){
    // make string like "09-under30-Mon"
    if(rowobj['days']=='empty'){
      // pass
    }else{
      start_cell_id = "#";
      end_cell_id = "#";
      // start time : times[i][0] , end time:tiems[i][1]
      shour = rowobj['times'][i][0].split(":")[0] // start hour
      smin = rowobj['times'][i][0].split(":")[1] // start min
      ehour = rowobj['times'][i][1].split(":")[0] // end hour
      emin = rowobj['times'][i][1].split(":")[1] // end min

      // fill start time
      if(parseInt(smin) >= 30){
        // 시작 시간이므로 30분도 포함해서 가야함
        start_cell_id += shour + "-" + "over30" + "-" + day_to_code(val);
        $(start_cell_id).addClass("hover");
      }else{
        start_cell_id += shour + "-" + "under30" + "-" + day_to_code(val);
        $(start_cell_id).addClass("hover");
      }
      // fill end time
      // 21시 넘는경우 끝까지 칠함
      if(parseInt(ehour)>=21){
        end_cell_id += "20-over30-"+day_to_code(val);
        $(end_cell_id).addClass("hover");
      }else{
        if(parseInt(emin) > 30){
          // 끝 시간이므로 30분일 경우 딱 거기까지 칠하면 됨
          end_cell_id += ehour + "-" + "over30" + "-" + day_to_code(val);
          $(end_cell_id).addClass("hover");
        }else{
          end_cell_id += ehour + "-" + "under30" + "-" + day_to_code(val);
          $(end_cell_id).addClass("hover");
        }
      }
    }

    // fill interval 화15:00-17:50 (Y5411)
    if (parseInt(shour)==parseInt(ehour)){
      // pass
    }else{
      if(parseInt(smin)<30){
        // + start hour-over case
        interval_cell_id = "#" + shour + "-"+ "over30" + "-" + day_to_code(val);
        $(interval_cell_id).addClass("hover");
      }
      interval = parseInt(shour) + 1;
      while(interval != parseInt(ehour)){
        // + time interval
        interval_cell_id = "#" + interval + "-" + "under30" + "-" + day_to_code(val);  // interval이 int 지만 알아서 형변환
        $(interval_cell_id).addClass("hover");
        interval_cell_id = "#" + interval + "-" + "over30" + "-" + day_to_code(val);
        $(interval_cell_id).addClass("hover");
        interval += 1;
      }

      if(parseInt(emin)>30){
        // + end hour-under case
        interval_cell_id = "#" + ehour + "-" + "under30" + "-" + day_to_code(val);
        $(interval_cell_id).addClass("hover");
      }
    }
  });
}

function insert_classitem(elem){
  console.log(time_parser(elem));

  //remove hover
  elem.removeClass("hover");
  $("td").removeClass("hover");
}

function activateCSS(){
  $('.table-row').hover(function(){
    $(this).addClass("hover");
    set_highlight(time_parser($(this)));
  }, function(){
    $(this).removeClass("hover");
    $("td").removeClass("hover");
  });

  $('.table-row').click(function(event){
    $('.table-row').not(this).removeClass('clicked');
    $('#check *').remove();
    $(this).toggleClass("clicked");

    // clicked 인 경우 add/delete icon 추가
    // clicked 가 없어진 경우 (toggle) icon 삭제
    if($(this).hasClass("clicked")){
      $(this).children('#check').append($('<button class="ui icon button" \
      onclick="check_insert()">\
      </button>'));
    }else{
      $('#check *').remove();
    }
  });
}

function add_timetable(){
  //tab 개수 구한 후 추가
  switch ($('#timetable-tab').children('a').length) {
    case 1:
      data_tab = "second";
      tab_html = "PLAN B";
      color = random_color();
      break;
    case 2:
      data_tab = "third";
      tab_html = "PLAN C";
      color = random_color();
      break;
    default:
      alert("더이상 추가할 수 없습니다.");
      return;
  }
  $("#timetable-tab").children($('a')).removeClass("active"); // tab 의 a 태그 에서 active 제거
  $("#timetable-tab").siblings(".active").removeClass("active"); // tab 내용에서 active 제거
  $(''+tab_html+'').insertBefore($("#add-timetable"));
  $("#my-timetable").append($('
\

\

\

\
            /	\
            월요일	\
            화요일	\
            수요일	\
            목요일	\
            금요일	\

\

\

'));
  tbody_html="";
  for(i=1; i<13; i++){
    hour = i+8;
    if(hour.toString().length == 1){ // 9 시 이하면(한자리수 시간) 0 붙여줌
      start="0"+hour;
    }else{
      start=hour;
    }
    end = hour+1;
    tbody_html +=
    '
\
      '+i+'교시
('+start+'~'+end+')	\
      	\
      	\
      	\
      	\
      	\

\

\
      	\
      	\
      	\
      	\
      	\

\
    ';
  }
  $("[data-tab="+data_tab+"]").children($("tbody")).append(tbody_html).append("
\

\

");

  $('.tabular.menu .item').tab();  // Activate tab in semantic ui
}

function remove_timetable(){
  if($("#timetable-tab").children(".active").attr("data-tab") == "first"){
    alert("PLAN A는 삭제할 수 없습니다.");
    return;
  }else{
    $("#timetable-tab").children(".active").remove();
    $("#timetable-tab").siblings(".active").remove();

    // $("[data-tab=xxxx]")
    $.each($("#timetable-tab").children('a'),function(index, value){
      if(index==0){
        $("#timetable-tab").children('a').eq(index).attr("data-tab","first");
        $("#timetable-tab").siblings().eq(index).attr("data-tab","first");
        $("#timetable-tab").children('a').eq(index).text("PLAN A");
      }
      if(index==1){
        $("#timetable-tab").children('a').eq(index).attr("data-tab","second");
        $("#timetable-tab").siblings().eq(index).attr("data-tab","second");
        $("#timetable-tab").children('a').eq(index).text("PLAN B");
      }
    });
    $("#timetable-tab").children('a').eq(0).addClass("active");
    $("#timetable-tab").siblings().eq(0).addClass("active");
  }
  $('.tabular.menu .item').tab();  // Activate tab in semantic ui
}
<commit_msg>javascript 고치는 중<commit_after>function check_insert(){
  if(confirm("추가하시겠습니까?")){
    insert_classitem($(".clicked"));
  }else{
    alert("close");
  }
}

function day_to_code(day){
  switch (day){
    case '월' : ret = 'Mon'; break;
    case '화' : ret = 'Tue'; break;
    case '수' : ret = 'Wed'; break;
    case '목' : ret = 'Thu'; break;
    case '금' : ret = 'Fri'; break;
  }
  return ret;
}

function random_table_color(){
  var colors = new Array("red","orange","yellow","olive","green","teal","blue",
  "violet","purple","pink","grey","black");
  return colors[Math.floor(Math.random() * colors.length)];
}

function random_item_color(){
  var colors = new Array()
  // selected라고 되어있는 element 찾아서 배열에서 중복되는 색 뺴준 후 랜덤 리턴.

}

function time_parser(elem){
  var row_dict = {};
  classname = $(elem)[0].children[1].innerHTML;
  prof = $(elem)[0].children[4].innerHTML;
  classtime = $(elem)[0].children[7].innerHTML.split(","); //["월13:00-14:50 (Y5420)", "수13:00-14:50 (Y5420)"]

  daypattern = /(월|화|수|목|금)/;
  timepattern = /\d{2}:\d{2}/g;
  classroompattern = /\w\d{3,5}/g;

  days = [];
  times=[];
  classrooms=[];

  // except empty
  $.each(classtime, function(index, value){
    if(value=="미입력"){
      days.push("empty");
      times.push("empty");
      classrooms.push("empty");
    }else{
      day= value.match(daypattern);
      time = value.match(timepattern);
      classroom = value.match(classroompattern);
      days.push(day[0]); // push() method : new value to array, append()는 html에 추가할때 사용, day가 array라서 index붙임
      times.push(time);
      if(classroom == null){
        classrooms.push("empty");
      }else{
        classrooms.push(classroom[0]);
      }
    }
  });

  row_dict['classname'] = classname;
  row_dict['prof'] = prof;
  row_dict['classtime'] = classtime;
  row_dict['days'] = days;
  row_dict['times'] = times;
  row_dict['classrooms'] = classrooms;

  return row_dict;
}

function set_highlight(rowobj, option){
  // row의 time parsing 된 dictionary 받아 hover event 줌
  $.each(rowobj['days'], function(i,val){
    // make string like "09-under30-Mon"
    if(rowobj['days']=='empty'){
      // pass
    }else{
      start_cell_id = "#";
      end_cell_id = "#";
      // start time : times[i][0] , end time:tiems[i][1]
      shour = rowobj['times'][i][0].split(":")[0] // start hour
      smin = rowobj['times'][i][0].split(":")[1] // start min
      ehour = rowobj['times'][i][1].split(":")[0] // end hour
      emin = rowobj['times'][i][1].split(":")[1] // end min

      // fill start time
      if(parseInt(smin) >= 30){
        // 시작 시간이므로 30분도 포함해서 가야함
        start_cell_id += shour + "-" + "over30" + "-" + day_to_code(val);
        $(start_cell_id).addClass(option);
      }else{
        start_cell_id += shour + "-" + "under30" + "-" + day_to_code(val);
        $(start_cell_id).addClass(option);
      }
      // fill end time
      // 21시 넘는경우 끝까지 칠함
      if(parseInt(ehour)>=21){
        end_cell_id += "20-over30-"+day_to_code(val);
        $(end_cell_id).addClass(option);
      }else{
        if(parseInt(emin) > 30){
          // 끝 시간이므로 30분일 경우 딱 거기까지 칠하면 됨
          end_cell_id += ehour + "-" + "over30" + "-" + day_to_code(val);
          $(end_cell_id).addClass(option);
        }else{
          end_cell_id += ehour + "-" + "under30" + "-" + day_to_code(val);
          $(end_cell_id).addClass(option);
        }
      }
    }

    // fill interval 화15:00-17:50 (Y5411)
    if (parseInt(shour)==parseInt(ehour)){
      // pass
    }else{
      if(parseInt(smin)<30){
        // + start hour-over case
        interval_cell_id = "#" + shour + "-"+ "over30" + "-" + day_to_code(val);
        $(interval_cell_id).addClass(option);
      }
      interval = parseInt(shour) + 1;
      while(interval != parseInt(ehour)){
        // + time interval
        interval_cell_id = "#" + interval + "-" + "under30" + "-" + day_to_code(val);  // interval이 int 지만 알아서 형변환
        $(interval_cell_id).addClass(option);
        interval_cell_id = "#" + interval + "-" + "over30" + "-" + day_to_code(val);
        $(interval_cell_id).addClass(option);
        interval += 1;
      }

      if(parseInt(emin)>30){
        // + end hour-under case
        interval_cell_id = "#" + ehour + "-" + "under30" + "-" + day_to_code(val);
        $(interval_cell_id).addClass(option);
      }
    }
  });
}

function insert_classitem(elem){
  row = time_parser(elem);
  set_highlight(row, "selected");
  //remove hover
  elem.removeClass("hover");
  $("td").removeClass("hover");
}

function activateCSS(){
  $('.table-row').hover(function(){
    $(this).addClass("hover");
    set_highlight(time_parser($(this)),"hover");
  }, function(){
    $(this).removeClass("hover");
    $("td").removeClass("hover");
  });

  $('.table-row').click(function(event){
    $('.table-row').not(this).removeClass('clicked');
    $('#check *').remove();
    $(this).toggleClass("clicked");

    // clicked 인 경우 add/delete icon 추가
    // clicked 가 없어진 경우 (toggle) icon 삭제
    if($(this).hasClass("clicked")){
      $(this).children('#check').append($('<button class="ui icon button" \
      onclick="check_insert()">\
      </button>'));
    }else{
      $('#check *').remove();
    }
  });
}

function add_timetable(){
  //tab 개수 구한 후 추가
  switch ($('#timetable-tab').children('a').length) {
    case 1:
      data_tab = "second";
      tab_html = "PLAN B";
      color = random_table_color();
      break;
    case 2:
      data_tab = "third";
      tab_html = "PLAN C";
      color = random_table_color();
      break;
    default:
      alert("더이상 추가할 수 없습니다.");
      return;
  }
  $("#timetable-tab").children($('a')).removeClass("active"); // tab 의 a 태그 에서 active 제거
  $("#timetable-tab").siblings(".active").removeClass("active"); // tab 내용에서 active 제거
  $(''+tab_html+'').insertBefore($("#add-timetable"));
  $("#my-timetable").append($('
\

\

\

\
            /	\
            월요일	\
            화요일	\
            수요일	\
            목요일	\
            금요일	\

\

\

'));
  tbody_html="";
  for(i=1; i<13; i++){
    hour = i+8;
    if(hour.toString().length == 1){ // 9 시 이하면(한자리수 시간) 0 붙여줌
      start="0"+hour;
    }else{
      start=hour;
    }
    end = hour+1;
    tbody_html +=
    '
\
      '+i+'교시
('+start+'~'+end+')	\
      	\
      	\
      	\
      	\
      	\

\

\
      	\
      	\
      	\
      	\
      	\

\
    ';
  }
  $("[data-tab="+data_tab+"]").children($("tbody")).append(tbody_html).append("
\

\

");

  $('.tabular.menu .item').tab();  // Activate tab in semantic ui
}

function remove_timetable(){
  if($("#timetable-tab").children(".active").attr("data-tab") == "first"){
    alert("PLAN A는 삭제할 수 없습니다.");
    return;
  }else{
    $("#timetable-tab").children(".active").remove();
    $("#timetable-tab").siblings(".active").remove();

    // $("[data-tab=xxxx]")
    $.each($("#timetable-tab").children('a'),function(index, value){
      if(index==0){
        $("#timetable-tab").children('a').eq(index).attr("data-tab","first");
        $("#timetable-tab").siblings().eq(index).attr("data-tab","first");
        $("#timetable-tab").children('a').eq(index).text("PLAN A");
      }
      if(index==1){
        $("#timetable-tab").children('a').eq(index).attr("data-tab","second");
        $("#timetable-tab").siblings().eq(index).attr("data-tab","second");
        $("#timetable-tab").children('a').eq(index).text("PLAN B");
      }
    });
    $("#timetable-tab").children('a').eq(0).addClass("active");
    $("#timetable-tab").siblings().eq(0).addClass("active");
  }
  $('.tabular.menu .item').tab();  // Activate tab in semantic ui
}
<|endoftext|>