/*
* Copyright (C) 2008-2024, Juick
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package com.juick.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.juick.model.User;
import jakarta.inject.Inject;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.List;
/**
* Created by vitalyster on 09.12.2016.
*/
@Repository
@Transactional
public class EmailServiceImpl extends BaseJdbcService implements EmailService {
@Inject
private OkHttpClient httpClient;
@Inject
private ObjectMapper jsonMapper;
@Value("${email_validation_service_url:http://localhost:8080}")
private String emailValidationServiceUrl;
@Override
public boolean verifyAddressByCode(Integer userId, String code) {
try {
String address = getJdbcTemplate().queryForObject("SELECT account FROM auth WHERE user_id=? AND protocol='email' AND authcode=?",
String.class, userId, code);
addEmail(userId, address);
getJdbcTemplate().update("DELETE FROM auth WHERE user_id=? AND authcode=?", userId, code);
} catch (EmptyResultDataAccessException e) {
return false;
}
return true;
}
@Override
public boolean addVerificationCode(Integer userId, String account, String code) {
return getJdbcTemplate().update("INSERT INTO auth(user_id,protocol,account,authcode) VALUES (?,'email',?,?)",
userId, account, code) > 0;
}
@Override
public boolean addEmail(Integer userId, String email) {
return getJdbcTemplate().update("INSERT INTO emails(user_id,email, subscr_hour) VALUES (?,?, 1)", userId, email) > 0;
}
@Override
public boolean deleteEmail(Integer userId, String account) {
return getNamedParameterJdbcTemplate().update("DELETE FROM emails " +
"WHERE (SELECT COUNT(*) cnt FROM (select user_id, email FROM emails e) c WHERE user_id=:uid) > 1 " +
"AND user_id=:uid AND email=:email",
new MapSqlParameterSource()
.addValue("uid", userId)
.addValue("email", account)) > 0;
}
@Override
public boolean disableEmail(User user, String account) {
return getJdbcTemplate().update("UPDATE emails SET subscr_hour=NULL " +
"WHERE user_id=? AND email=?", user.getUid(), account) > 0;
}
@Transactional(readOnly = true)
@Override
public String getNotificationsEmail(Integer userId) {
List list = getJdbcTemplate().queryForList(
"SELECT email FROM emails WHERE user_id=? AND subscr_hour IS NOT NULL", String.class, userId);
return list.isEmpty() ? StringUtils.EMPTY : list.get(0);
}
@Override
public boolean setNotificationsEmail(Integer userId, String account) {
getJdbcTemplate().update("UPDATE emails SET subscr_hour=NULL WHERE user_id=?", userId);
return StringUtils.isNotEmpty(account) && getJdbcTemplate().update(
"UPDATE emails SET subscr_hour=1 WHERE user_id=? AND email=?", userId, account) > 0;
}
@Transactional(readOnly = true)
@Override
public List getEmails(Integer userId, boolean active) {
return getJdbcTemplate().queryForList("SELECT email FROM emails WHERE user_id=? " +
(active ? "AND subscr_hour IS NOT NULL" : ""), String.class, userId);
}
@Transactional(readOnly = true)
@Override
public String getEmailByAuthCode(String code) {
try {
return getJdbcTemplate().queryForObject("SELECT account FROM auth WHERE protocol='email' AND authcode=?", String.class, code);
} catch (EmptyResultDataAccessException e) {
return StringUtils.EMPTY;
}
}
@Transactional
@Override
public void deleteAuthCode(String code) {
getJdbcTemplate().update("DELETE FROM auth WHERE authcode=?", code);
}
@Override
public Integer cleanupAuthCodes() {
Instant day = Instant.now().minus(1, ChronoUnit.DAYS);
return getNamedParameterJdbcTemplate().update("DELETE FROM auth WHERE ts < :day",
new MapSqlParameterSource()
.addValue("day", toDateTime(day.atOffset(ZoneOffset.UTC)), dateTimeType()));
}
@Override
public boolean isValidEmail(String email) {
var request = new Request.Builder()
.url(emailValidationServiceUrl + "/?email=" + email)
.addHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
.build();
try {
try(var response = httpClient.newCall(request).execute()) {
if (response.isSuccessful() && response.body() != null) {
var account = jsonMapper.readTree(response.body().string());
return account.has("disposable") && account.get("disposable").textValue().equals("false");
}
}
} catch (IOException e) {
return false;
}
return false;
}
}