// Redirect between plain HTTP and encrypted HTTPS according to
// `general_ssl_mode` global setting.
//
// http_only: Force HTTP everywhere.
// https_only: Force HTTPS everywhere.
// https_for_registered: Force HTTPS for users, and HTTP for guests.


'use strict';


var COOKIE_MAXAGE_LIKE_FOREVER = 0xFFFFFFFF;
var COOKIE_EXPIRES_IN_THE_PAST = new Date('Thu, 01 Jan 1970 00:00:00 GMT');


module.exports = function (N) {

  // Ensure HTTPS if "ssl" cookie is set *before* session load because
  // session id is stored as secure cookie - it's sent only encrypted.
  N.wire.before('server_chain:*', { priority: -82 }, function force_members_https(env, callback) {
    if ('1' !== env.extras.getCookie('ssl')) {
      // No cookie. Continue.
      callback();
      return;
    }

    if (env.request.isEncrypted) {
      // Already encrypted. Continue.
      callback();
      return;
    }

    var redirectUrl = env.helpers.url_to(env.method, env.params, { protocol: 'https' });

    if (!redirectUrl) {
      // Can't build URL - usually RPC-only method.
      callback({
        code: N.io.BAD_REQUEST
      , message: 'Encrypted connection is required'
      });
      return;
    }

    callback({
      code: N.io.REDIRECT
    , head: { Location: redirectUrl }
    });
  });

  // Helper to read "general_ssl_mode" setting used by next hooks.
  function isEncryptionRequired(env, callback) {
    N.settings.get('general_ssl_mode', {}, function (err, policy) {
      if (err) {
        callback(err);
        return;
      }

      switch (policy) {
      case 'https_off':
        callback(null, false);
        return;

      case 'https_always':
        callback(null, true);
        return;

      case 'https_members':
        if ('users.auth' === env.method.slice(0, 'users.auth'.length)) {
          // force true even for guests at login/register page
          callback(null, true);
        } else {
          callback(null, Boolean(env.session && env.session.user_id));
        }
        return;

      default:
        // Case for broken settings store.
        callback('Value of setting "general_ssl_mode" is unknown: ' +
                 JSON.stringify(policy));
        return;
      }
    });
  }


  // Main guest/user http/https redirector. Just after session load.
  N.wire.before('server_chain:*', { priority: -75 }, function switch_http_and_https(env, callback) {
    isEncryptionRequired(env, function (err, shouldBeEncrypted) {
      if (err) {
        callback(err);
        return;
      }

      if (env.request.isEncrypted === shouldBeEncrypted) {
        // Redirection is not needed. Continue.
        callback();
        return;
      }

      var redirectUrl = env.helpers.url_to(env.method, env.params, {
        protocol: shouldBeEncrypted ? 'https' : 'http'
      });

      if (!redirectUrl) {
        // Can't build URL - usually RPC-only method.
        callback({
          code: N.io.BAD_REQUEST
        , message: 'Encrypted connection is required'
        });
        return;
      }

      callback({
        code: N.io.REDIRECT
      , head: { Location: redirectUrl }
      });
    });
  });


  // Set cookie to keep secure connection for logged in users.
  N.wire.after('server_chain:*', { priority: 80, ensure: true }, function set_ssl_redirect_cookie(env, callback) {
    isEncryptionRequired(env, function (err, shouldBeEncrypted) {
      if (err) {
        callback(err);
        return;
      }

      if (env.session && env.session.user_id && shouldBeEncrypted) {
        // Add cookie for a HUGE amount of time.
        env.extras.setCookie('ssl', '1', { maxAge: COOKIE_MAXAGE_LIKE_FOREVER, path: '/' });
      } else {
        // Delete cookie by setting 'expires' to a past date.
        env.extras.setCookie('ssl', '0', { expires: COOKIE_EXPIRES_IN_THE_PAST, path: '/' });
      }
      callback();
    });
  });


  // Modify 'secure' flag of session id cookie.
  N.wire.after('server_chain:*', { priority: 85, ensure: true }, function set_sid_cookie_mode(env, callback) {
    isEncryptionRequired(env, function (err, shouldBeEncrypted) {
      if (err) {
        callback(err);
        return;
      }

      env.extras.updateCookie('sid', { secure: shouldBeEncrypted });
      callback();
    });
  });
};

@@ -1344,16 +1344,90 @@
  hooks.%0A
+  // If %60pageCheck%60 is true, page-specific encryption status in included.%0A
   functi
@@ -1450,24 +1450,35 @@
 equired(env,
+ pageCheck,
  callback) %7B
@@ -1821,16 +1821,29 @@
     if (
+pageCheck && 
 'users.a
@@ -2504,32 +2504,38 @@
 ionRequired(env,
+ true,
  function (err, 
@@ -3440,32 +3440,39 @@
 ionRequired(env,
+ false,
  function (err, 
@@ -3571,46 +3571,8 @@
 if (
-env.session && env.session.user_id && 
 shou
@@ -4131,16 +4131,23 @@
 red(env,
+ false,
  functio

Fix 'Secure' flag on 'sid' cookie.