/*******************************************************************************
* Copyright (c) 2013 Hani Naguib.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* Hani Naguib - initial API and implementation
******************************************************************************/
package com.gvmax.web;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.gvmax.common.model.APIAction;
import com.gvmax.common.model.APIAction.ACTIONS;
import com.gvmax.common.model.GlobalStats;
import com.gvmax.common.model.Stats;
import com.gvmax.common.model.User;
import com.gvmax.common.model.XMPPAction;
import com.gvmax.common.relay.GVMaxRelay;
import com.gvmax.common.util.EmailUtils;
import com.gvmax.common.util.MiscUtils;
import com.gvmax.common.util.PhoneUtil;
import com.gvmax.common.util.growl.Prowl;
import com.gvmax.data.queue.QueueDAO;
import com.gvmax.data.user.UserDAO;
import com.gvmax.google.contacts.GoogleContacts;
import com.gvmax.google.talk.GTalk;
import com.gvmax.google.voice.GoogleVoice;
@Service
public class GVMaxServiceImpl {
private UserDAO userDAO;
private GVMaxRelay relay;
private String adminAccount;
private QueueDAO<APIAction> apiQueue;
private QueueDAO<XMPPAction> xmppQueue;
private AtomicLong apiCount = new AtomicLong(System.currentTimeMillis());
// private Twitter tw = new TwitterFactory().getInstance("my_gv_max",
// "triostrios");
@Autowired
public GVMaxServiceImpl(UserDAO userDAO, GVMaxRelay relay, @Value("${web.adminAccount}") String adminAccount, @Qualifier("apiQueue") QueueDAO<APIAction> apiQueue, @Qualifier("xmppQueue") QueueDAO<XMPPAction> xmppQueue) {
this.userDAO = userDAO;
this.relay = relay;
this.adminAccount = adminAccount;
this.apiQueue = apiQueue;
this.xmppQueue = xmppQueue;
}
public User getUser(String email) {
return userDAO.retrieve(email);
}
public User getUser(String email, String pin) {
email = EmailUtils.normalizeEmail(email);
if (!StringUtils.isBlank(pin)) {
User user = userDAO.retrieveByPin(pin);
if (user != null && user.getEmail().equals(adminAccount)) {
if (StringUtils.isBlank(email)) {
return user;
}
return userDAO.retrieve(email);
}
return user;
} else {
// if (email != null) {
return userDAO.retrieve(email);
// }
}
}
public List<User> getUsers(int offset, int limit) {
return userDAO.getUsers(offset,limit);
}
public Stats getStats(String email) {
return userDAO.getStats(email);
}
public GlobalStats getStats() {
return userDAO.getStats();
}
public User login(String email, String password) throws IOException {
email = EmailUtils.normalizeEmail(email);
User user = userDAO.retrieve(email);
if (user == null) {
MiscUtils.sleep(3000);
throw new IOException("Invalid credentials");
}
if (!user.isGvPassword()) {
if (!password.equals(user.getPassword())) {
MiscUtils.sleep(3000);
throw new IOException("Invalid credentials");
}
} else {
GoogleVoice gv = new GoogleVoice(email, password);
try {
gv.login();
} catch (IOException e) {
MiscUtils.sleep(3000);
throw new IOException("Invalid crendentials",e);
}
if (!password.equals(user.getPassword())) {
userDAO.setPassword(email, password);
}
}
return user;
}
public RegistrationInfo signup(String email, String password, boolean isGVPassword) {
email = EmailUtils.normalizeEmail(email);
RegistrationInfo info = new RegistrationInfo();
info.setGvPassword(isGVPassword);
User user = userDAO.retrieve(email);
// ALREADY EXISTS
if (user != null) {
info.setAlreadyRegistered(true);
info.setRegistered(true);
// Check if password correct
try {
checkCredentials(user, email, password);
} catch (Exception e) {
info.setRegistered(false);
info.setInvalidCredentials(true);
}
return info;
}
user = new User();
// IF GV CHECK PASSWORD
GoogleVoice gv = new GoogleVoice(email, password);
if (isGVPassword) {
try {
user.setGvPhone(gv.getPhoneNumber());
} catch (Exception e) {
info.setRegistered(false);
info.setInvalidCredentials(true);
return info;
}
}
// Blacklist
if (isBlacklisted(email, user)) {
// userDAO.delete(user.getEmail());
info.setRegistered(false);
info.setBlacklisted(true);
return info;
}
// CREATE ACCOUNT
user.setEmail(email);
Stats stats = userDAO.getStats(email);
if (stats != null) {
// reuse pin
user.setPin(stats.getPin());
} else {
user.setPin(createPin(email));
}
user.setPassword(password);
user.setGvPassword(isGVPassword);
user.setMonitorSMS(true);
user.setMonitorVM(true);
user.setEmailAddresses(email);
user.setgTalkGroup("GVMax");
if (GTalk.validateCredentials(email, password)) {
user.setgTalkEmail(email);
user.setgTalkPassword(password);
user.setSendGTalk(true);
} else {
user.setSendEmail(true);
}
// CREATE GV FILTERS
if (isGVPassword) {
// TODO: createFilters(email, password, user.getPin(), info);
MiscUtils.emptyBlock();
}
// STORE
user.setCreationDate(System.currentTimeMillis());
userDAO.store(user);
info.setRegistered(true);
return info;
}
public RegistrationInfo register(User user, String googlePassword) {
user.setEmail(EmailUtils.normalizeEmail(user.getEmail()));
RegistrationInfo info = new RegistrationInfo();
info.setGvPassword(user.isGvPassword());
User existingUser = userDAO.retrieve(user.getEmail());
if (existingUser != null) {
info.setAlreadyRegistered(true);
}
// Check if gv password is ok
GoogleVoice gv = new GoogleVoice(user.getEmail(), googlePassword);
try {
user.setGvPhone(gv.getPhoneNumber());
} catch (IOException e) {
info.setRegistered(false);
info.setInvalidCredentials(true);
return info;
}
// Blacklist
if (isBlacklisted(user.getEmail(), user)) {
// userDAO.delete(user.getEmail());
info.setRegistered(false);
info.setBlacklisted(true);
return info;
}
if (existingUser != null) {
user.setPin(existingUser.getPin());
} else {
user.setPin(createPin(user.getEmail()));
}
info.setPin(user.getPin());
user.setCreationDate(System.currentTimeMillis());
userDAO.store(user);
info.setRegistered(true);
// TODO: Try to fix filter creation
info.setFiltersCreated(false);
// if (googlePassword != null) {
// try {
// // Remove existing filters
// GMail gm = new GMail();
// gm.deleteFilterWithFwdEmail(user.getEmail(), googlePassword, user.getPin() + "@my.gvmax.com");
// gm.deleteFilterWithFwdEmail(user.getEmail(), googlePassword, "gvmax@gvmax-post.appspotmail.com");
// } catch (Exception e) {
// // Well too bad.
// MiscUtils.emptyBlock();
// }
//
// try {
// createFilters(user.getEmail(), googlePassword, user.getPin(), info);
// } catch (Exception e) {
// info.setFiltersCreated(false);
// }
// }
return info;
}
public void unregister(String email) {
email = EmailUtils.normalizeEmail(email);
User user = userDAO.retrieve(email);
if (user == null) {
return;
}
// DELETE FILTER
//deleteFilters(user.getEmail(), user.getPassword(), user.getPin());
userDAO.delete(email);
return;
}
public void forgotPassword(String email) throws IOException {
User user = userDAO.retrieve(email);
if (user == null) {
relay.sendEmail(relay.getEmailSender(), relay.getEmailSender(), email, "GVMax Password Retrieval", "Your email address is not registered with GVMax");
return;
}
if (user.isGvPassword()) {
relay.sendEmail(relay.getEmailSender(), relay.getEmailSender(), email, "GVMax Password Retrieval", "Your account at GVMax uses your Google Voice password, please contact Google Voice to obtain or reset your password.");
return;
}
relay.sendEmail(relay.getEmailSender(), relay.getEmailSender(), email, "GVMax Password Retrieval", "Your password is: " + user.getPassword());
}
public void changePassword(String email, String password, boolean gvPassword) throws IOException {
email = EmailUtils.normalizeEmail(email);
if (StringUtils.isBlank(email)) {
throw new IOException("invalid credentials");
}
if (StringUtils.isBlank(password)) {
throw new IOException("invalid credentials");
}
User user = getUser(email, null);
if (user == null) {
throw new IOException("invalid credentials");
}
if (gvPassword) {
GoogleVoice gv = new GoogleVoice(email, password);
gv.login();
}
user.setPassword(password);
user.setGvPassword(gvPassword);
userDAO.store(user);
}
public void setMonitors(String email, boolean monitorSMS, boolean monitorVM, boolean monitorMC) {
email = EmailUtils.normalizeEmail(email);
User user = getUser(email, null);
if (user == null) {
return;
}
user.setMonitorSMS(monitorSMS);
user.setMonitorVM(monitorVM);
user.setMonitorMC(monitorMC);
userDAO.store(user);
}
public void setNotifiers(User user) throws IOException {
// GTalk
if (user.isSendGTalk()) {
if (StringUtils.isBlank(user.getgTalkEmail())) {
throw new IOException("you must specify your gtalk address");
}
if (StringUtils.isBlank(user.getgTalkPassword())) {
throw new IOException("gtalk password missing");
}
if (!GTalk.validateCredentials(user.getgTalkEmail(), user.getgTalkPassword())) {
throw new IOException("GTalk Address/Password could not be verified");
}
}
// Check Prowl
boolean prowlKeyPresent = !StringUtils.isBlank(user.getProwlApiKeys());
if (user.isSendProwl() && !prowlKeyPresent) {
throw new IOException("Prowl keys missing");
}
if (prowlKeyPresent) {
String[] prowlKeys = user.getProwlApiKeys().split(",");
for (String key : prowlKeys) {
if (!Prowl.verify(key)) {
throw new IOException("Prowl key '" + key + "' could not be verified");
}
}
}
// Check Howl
if (user.isSendHowl()) {
if (StringUtils.isBlank(user.getHowlUsername())) {
throw new IOException("you must specify the howl username");
}
if (StringUtils.isBlank(user.getHowlPassword())) {
throw new IOException("you must specify the howl password");
}
}
// Check out emails
boolean emailsPresent = !StringUtils.isBlank(user.getEmailAddresses());
if (user.isSendEmail() && !emailsPresent) {
throw new IOException("You must specify outbound emails for Email notifications.");
}
if (emailsPresent) {
String[] emails = user.getEmailAddresses().split(",");
for (String email : emails) {
if (!EmailUtils.isEmail(email)) {
throw new IOException("Email '" + email + "' not valid");
}
}
}
// Check SMS Group
if (user.isSendSMS()) {
String userGroup = user.getSmsGroup();
if (StringUtils.isBlank(user.getSmsGroup())) {
throw new IOException("You must specify an SMS group or number for SMS notifications.");
}
if (!PhoneUtil.isNumber(userGroup)) {
List<String> groups = null;
if (user.isGvPassword()) {
groups = GoogleContacts.getGroups(user.getEmail(), user.getPassword());
} else {
groups = GoogleContacts.getGroups(user.getgTalkEmail(), user.getgTalkPassword());
}
boolean found = false;
for (String group : groups) {
if (group.equals(userGroup)) {
found = true;
break;
}
}
if (!found) {
throw new IOException("SMS Group '" + userGroup + "' does not exist");
}
}
}
// Check post urls
boolean postsPresent = !StringUtils.isBlank(user.getPostURLs());
if (user.isSendPost() && !postsPresent) {
throw new IOException("You must specify post urls for Post notifications.");
}
if (postsPresent) {
String[] urls = user.getPostURLs().split(",");
for (String url : urls) {
if (!MiscUtils.isUrl(url)) {
throw new IOException("URL '" + url + "' in post urls is invalid.");
}
}
}
// Check Twitter
if (user.isSendTwitter() && StringUtils.isBlank(user.getTwitterScreenName())) {
throw new IOException("You must specify your Twitter screen name");
// try {
// tw.createFriendship(user.twScreenName);
// } catch (TwitterException e) {
// }
}
// Check AutoRespond
if (user.isSendAutoResponse() && StringUtils.isBlank(user.getAutoResponse())) {
throw new IOException("You must specify an auto response message");
}
userDAO.store(user);
}
public String sendSMS(String pin, String number, String text, String callback) throws IOException {
APIAction action = new APIAction(ACTIONS.SEND);
action.setId("" + apiCount.incrementAndGet());
action.setPin(pin);
action.setNumber(number);
action.setText(text);
action.setCallback(callback);
apiQueue.enqueue(action);
return action.getId();
}
public void sendXMPPIn(XMPPAction action) throws IOException {
xmppQueue.enqueue(action);
}
public User checkCredentials(User user, String email, String password) throws IOException {
if (!user.isGvPassword()) {
if (user.getPassword().equals(password)) {
return user;
}
throw new IOException("invalid credentials: 2");
}
// Is GV try login
GoogleVoice gv = new GoogleVoice(email, password);
gv.login();
// If pass != account.pass update account.pass
if (!user.getPassword().equals(password)) {
user.setPassword(password);
userDAO.store(user);
}
return user;
}
public String createPin(String email) {
return UUID.randomUUID().toString().replaceAll("-", "");
}
// public void createFilters(String email, String password, String pin, RegistrationInfo info) {
// GoogleVoice gv = new GoogleVoice(email, password);
// try {
// info.setGvFwdEmail(gv.getForwardingAddress().toLowerCase());
// } catch (Exception e) {
// info.setInvalidCredentials(true);
// return;
// }
// if (email.equals(info.getGvFwdEmail())) {
// // Setup GV
// try {
// gv.enableForwarding(true, true);
// } catch (IOException e) {
// info.setFiltersCreated(false);
// return;
// }
//
// GMail gmail = new GMail();
// // SMS FILTER
// GMailFilter filter = new GMailFilter();
// filter.setFrom("txt.voice.google.com OR voice-noreply@google.com");
// // HANI
// filter.setFwdEmail(pin + "@" + relay.getEmailHost());
// filter.setSkipInbox(true);
// try {
// gmail.createFilter(email, password, filter);
// info.setFiltersCreated(true);
// } catch (Exception e) {
// info.setFiltersCreated(false);
// }
// }
//
// }
//
// private boolean deleteFilters(String email, String password, String fwdEmail) {
// GMail gmail = new GMail();
// try {
// gmail.deleteFilterWithFwdEmail(email, password, fwdEmail);
// return true;
// } catch (Exception e) {
// return false;
// }
// }
public void blacklist(String value) {
userDAO.blacklist(value);
}
private boolean isBlacklisted(String email, User user) {
boolean blackListed = userDAO.isBlacklisted(email);
if (!blackListed && user != null && user.getGvPhone() != null) {
blackListed = userDAO.isBlacklisted(user.getGvPhone());
}
return blackListed;
}
}