private void handleSignupPost(Request request, HttpServletResponse httpServletResponse)
      throws Exception {
    String userId = request.getParameter(PARAM_USER_ID);
    String userName = request.getParameter(PARAM_USER_NAME);
    String email = request.getParameter(PARAM_EMAIL);
    String stringPassword = request.getParameter(PARAM_PASSWORD);
    String stringPasswordConfirm = request.getParameter(PARAM_PASSWORD_CONFIRM);

    if (!stringPassword.equals(stringPasswordConfirm)) {
      WebUtils.redirectToError(
          "Mismatch between password and password confirmation", request, httpServletResponse);
      return;
    }

    SecureRandom secureRandom = new SecureRandom();
    String salt = "" + secureRandom.nextLong();
    byte[] password = User.computeHashedPassword(stringPassword, salt);
    User user = userDb.get(userId);
    if (user != null) {
      WebUtils.redirectToError(
          "There already exists a user with the ID " + userId, request, httpServletResponse);
      return;
    }

    user =
        new User(
            userId,
            userName,
            password,
            salt,
            email,
            new ArrayList<String>(),
            Config.getConfig().activateAccountsAtCreation,
            false);
    // ttt2 add confirmation by email, captcha, ...
    List<String> fieldErrors = user.checkFields();
    if (!fieldErrors.isEmpty()) {
      StringBuilder bld =
          new StringBuilder("Invalid values when trying to create user with ID ")
              .append(userId)
              .append("<br/>");
      for (String s : fieldErrors) {
        bld.append(s).append("<br/>");
      }
      WebUtils.redirectToError(bld.toString(), request, httpServletResponse);
      return;
    }

    // ttt2 2 clients can add the same userId simultaneously
    userDb.add(user);

    httpServletResponse.sendRedirect("/");
  }
  private void handleChangePasswordPost(Request request, HttpServletResponse httpServletResponse)
      throws Exception {

    LoginInfo loginInfo = userHelpers.getLoginInfo(request);
    if (loginInfo == null) {
      WebUtils.redirectToError("Couldn't determine the current user", request, httpServletResponse);
      return;
    }

    String userId = loginInfo.userId;
    String stringCrtPassword = request.getParameter(PARAM_CURRENT_PASSWORD);
    String stringNewPassword = request.getParameter(PARAM_PASSWORD);
    String stringNewPasswordConfirm = request.getParameter(PARAM_PASSWORD_CONFIRM);

    if (!stringNewPassword.equals(stringNewPasswordConfirm)) {
      showResult(
          "Mismatch between password and password confirmation",
          PATH_SETTINGS,
          request,
          httpServletResponse);
      return;
    }

    User user =
        userDb.get(
            userId); // ttt1 crashes for wrong ID; 2013.07.20 - no longer have an idea what this is
                     // about
    if (user == null) {
      WebUtils.redirectToError("Couldn't find the current user", request, httpServletResponse);
      return;
    }

    if (!user.checkPassword(stringCrtPassword)) {
      showResult("Incorrect current password", PATH_SETTINGS, request, httpServletResponse);
      return;
    }

    SecureRandom secureRandom = new SecureRandom();
    String salt = "" + secureRandom.nextLong();
    byte[] password = User.computeHashedPassword(stringNewPassword, salt);
    user.salt = salt;
    user.password = password;

    // ttt3 2 clients can change the password simultaneously
    userDb.add(user);

    // httpServletResponse.sendRedirect(PATH_SETTINGS);
    showResult("Password changed", PATH_SETTINGS, request, httpServletResponse);
  }
  private void handleChangeSettingsPost(Request request, HttpServletResponse httpServletResponse)
      throws Exception {

    LoginInfo loginInfo = userHelpers.getLoginInfo(request);
    if (loginInfo == null) {
      WebUtils.redirectToError("Couldn't determine the current user", request, httpServletResponse);
      return;
    }

    String stringItemsPerPage = request.getParameter(PARAM_ITEMS_PER_PAGE);
    try {
      loginInfo.itemsPerPage = Integer.parseInt(stringItemsPerPage);
    } catch (Exception e) {
      showResult(
          "Error trying to set the items per page. Expected integer value but got "
              + stringItemsPerPage,
          PATH_SETTINGS,
          request,
          httpServletResponse);
      return;
    }
    loginInfo.style = request.getParameter(PARAM_STYLE);
    loginInfo.feedDateFormat =
        request.getParameter(PARAM_FEED_DATE_FORMAT); // ttt2 validate, better in JSP

    loginInfoDb.add(loginInfo);

    // httpServletResponse.sendRedirect(PATH_SETTINGS);
    showResult("Settings changed", "/", request, httpServletResponse);
  }
 private void handleOpenArticle(
     Request request, HttpServletResponse httpServletResponse, String target) throws Exception {
   try {
     int k1 = target.indexOf('/', 1);
     int k2 = target.indexOf('/', k1 + 1);
     String feedId = target.substring(k1 + 1, k2);
     String strSeq = target.substring(k2 + 1);
     int seq = Integer.parseInt(strSeq);
     Article article = articleDb.get(feedId, seq);
     LoginInfo loginInfo = userHelpers.getLoginInfo(request);
     // ttt2 using the link from a non-authenticated browser causes a NPE; maybe do something
     // better, e.g. sign up
     ReadArticlesColl readArticlesColl = readArticlesCollDb.get(loginInfo.userId, feedId);
     if (readArticlesColl == null) {
       readArticlesColl = new ReadArticlesColl(loginInfo.userId, feedId);
     }
     if (!readArticlesColl.isRead(seq)) {
       readArticlesColl.markRead(seq, Config.getConfig().maxSizeForReadArticles);
       readArticlesCollDb.add(readArticlesColl);
     }
     String s =
         URIUtil.encodePath(article.url)
             .replace("%3F", "?")
             .replace("%23", "#"); // ttt2 see how to do this right
     httpServletResponse.sendRedirect(s);
   } catch (Exception e) {
     WebUtils.showResult(
         String.format("Failed to get article for path %s. %s", target, e),
         "/",
         request,
         httpServletResponse);
   }
 }
  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;
  }