private void logOut(String browserId) throws Exception {
    // ttt2 the right way to do it is to go through all the sessions of the current browser, which
    // would require a new field and a new index;
    // not sure if it's worth it, but this would work: A logs in, forgets to log out, B delets the
    // cookies, logs in, A sees B is logged in, then B
    // restores the cookies and uses A's account
    if (browserId == null) {
      return;
    }

    List<LoginInfo> loginInfos = loginInfoDb.getLoginsForBrowser(browserId);
    long expireTarget = System.currentTimeMillis() - Utils.ONE_DAY;
    for (LoginInfo loginInfo : loginInfos) {
      if (loginInfo.expiresOn <= expireTarget) {
        LOG.info(String.format("LoginInfo %s is enough in the past", loginInfo));
      } else {
        LOG.info(String.format("Logging out: %s", loginInfo));
        loginInfoDb.updateExpireTime(browserId, loginInfo.sessionId, expireTarget);
      }
    }
  }
  private void handleLoginPost(
      Request request, HttpServletResponse httpServletResponse, boolean secured) throws Exception {
    String userId = request.getParameter(PARAM_USER_ID);
    String password = request.getParameter(PARAM_PASSWORD);
    String rememberAccountStr = request.getParameter(PARAM_REMEMBER_ACCOUNT);
    boolean rememberAccount = Boolean.parseBoolean(rememberAccountStr);
    LoginInfo.SessionInfo sessionInfo = UserHelpers.getSessionInfo(request);

    logOut(sessionInfo.browserId);

    User user = userDb.get(userId);
    if (user == null) {
      WebUtils.redirectToError("User " + userId + " not found", request, httpServletResponse);
      return;
    }

    if (!user.checkPassword(password)) {
      WebUtils.redirectToError("Invalid password", request, httpServletResponse);
      return;
    }

    if (!user.active) {
      WebUtils.redirectToError(
          "Account for User " + userId + " needs to be activated", request, httpServletResponse);
      return;
    }

    LOG.info("Logged in user " + userId);

    sessionInfo.sessionId = null;
    if (sessionInfo.browserId == null) {
      sessionInfo.browserId = getRandomId();
    } else {
      for (LoginInfo loginInfo : loginInfoDb.getLoginsForBrowser(sessionInfo.browserId)) {
        if (userId.equals(loginInfo.userId)) {
          sessionInfo.sessionId = loginInfo.sessionId;
          break;
        }
      }
    }

    long expireOn = System.currentTimeMillis() + Config.getConfig().loginExpireInterval;
    if (sessionInfo.sessionId == null) {
      sessionInfo.sessionId = getRandomId();
      Config config = Config.getConfig();
      loginInfoDb.add(
          new LoginInfo(
              sessionInfo.browserId,
              sessionInfo.sessionId,
              userId,
              expireOn,
              rememberAccount,
              config.defaultStyle,
              config.defaultItemsPerPage,
              config.defaultFeedDateFormat));
      LOG.info(String.format("Logging in in a new session. User: %s", user));
    } else {
      loginInfoDb.updateExpireTime(sessionInfo.browserId, sessionInfo.sessionId, expireOn);
      LOG.info(String.format("Logging in in an existing session. User: %s", user));
    }

    WebUtils.saveCookies(
        httpServletResponse, secured, sessionInfo.browserId, sessionInfo.sessionId);

    httpServletResponse.sendRedirect("/");
  }
  /**
   * Normally sets the path and a few attributes that the JSPs are likely to need. Also verifies the
   * login information. If necessary, just redirects to the login page.
   *
   * @param target
   * @param request
   * @param httpServletResponse
   * @param secured
   * @return true if the request is already handled so the .jsp shouldn't get called
   * @throws Exception
   */
  private boolean prepareForJspGet(
      String target, Request request, HttpServletResponse httpServletResponse, boolean secured)
      throws Exception {

    LoginInfo.SessionInfo sessionInfo = UserHelpers.getSessionInfo(request);

    LOG.info(
        String.format(
            "hndl - %s ; %s; %s ; %s",
            target,
            request.getPathInfo(),
            request.getMethod(),
            secured ? "secured" : "not secured"));

    String path = request.getUri().getDecodedPath();

    boolean redirectToLogin = path.equals(PATH_LOGOUT);
    LoginInfo loginInfo = null;
    if (sessionInfo.isNull()) {
      redirectToLogin = true;
      LOG.info("Null session info. Logging in again.");
    } else {
      loginInfo =
          loginInfoDb.get(
              sessionInfo.browserId,
              sessionInfo.sessionId); // ttt2 use a cache, to avoid going to DB
      if (loginInfo == null || loginInfo.expiresOn < System.currentTimeMillis()) {
        LOG.info("Session has expired. Logging in again. Info: " + loginInfo);
        redirectToLogin = true;
      }
    }

    if (!path.equals(PATH_LOGIN) && !path.equals(PATH_SIGNUP) && !path.equals(PATH_ERROR)) {

      if (redirectToLogin) {
        // ttt2 perhaps store URI, to return to it after login
        logOut(sessionInfo.browserId);
        addLoginParams(request, loginInfo);
        httpServletResponse.sendRedirect(PATH_LOGIN);
        return true;
      }

      User user = userDb.get(loginInfo.userId);
      if (user == null) {
        WebUtils.redirectToError("Unknown user", request, httpServletResponse);
        return true;
      }
      if (!user.active) {
        WebUtils.redirectToError("Account is not active", request, httpServletResponse);
        return true;
      }
      request.setAttribute(VAR_FEED_DB, feedDb);
      request.setAttribute(VAR_USER_DB, userDb);
      request.setAttribute(VAR_ARTICLE_DB, articleDb);
      request.setAttribute(VAR_READ_ARTICLES_COLL_DB, readArticlesCollDb);

      request.setAttribute(VAR_USER, user);
      request.setAttribute(VAR_LOGIN_INFO, loginInfo);

      MultiMap<String> params = new MultiMap<>();
      params.put(PARAM_PATH, path);
      request.setParameters(params);
    }

    if (path.equals(PATH_LOGIN)) {
      addLoginParams(request, loginInfo);
    }
    return false;
  }