From 1c77ff883e06efac04f26ebf519d22092a3bcae0 Mon Sep 17 00:00:00 2001 From: smint86 Date: Tue, 2 May 2017 16:29:24 +0200 Subject: [PATCH 01/17] Update README.md minor text fixes --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 0de1b4f..354806c 100644 --- a/README.md +++ b/README.md @@ -3,29 +3,29 @@ [![Last Version](https://img.shields.io/badge/version-1.0.0--Alpha1-brightgreen.svg)](https://github.com/drallieiv/KinanCity/releases/latest) [![Build Status](https://travis-ci.org/drallieiv/KinanCity.svg?branch=master)](https://travis-ci.org/drallieiv/KinanCity) -Any issues with KinanCity, or you just want to talk about the project ? Go to [our discord server]( http://discord.gg/3jkb3zA) +Any issues with KinanCity, or you just want to talk about the project? Go to [our discord server]( http://discord.gg/3jkb3zA) -## Were is Kinan City ? +## Where is Kinan City? **Kinan City** (キナンシティ) is one of the cities of the Kalos region in Pokemon XY games and anime. It is known as **Kiloude City** is the English version and **Batisques** in French. -Kinan City is known for it **Friend Safari** where many trainer comes to find pokemon. This is a good place if you want to meet a **lot of new trainers**. +Kinan City is known for its **Friend Safari** where many trainer come to find pokemon. This is a good place if you want to meet a **lot of new trainers**. -## What does KinanCity do ? +## What does KinanCity do? -**KinanCity** is a tool that automates the creation of Pokemon Trainer Accounts and contains multiple modules. +**KinanCity** is a tool that automates the creation of Pokemon Trainer Club Accounts and contains multiple modules. -- **KinanCity-core** : is the core module that can also be used in command line. [more info here](KinanCity-core/README.md) -- **KinanCity-mail** : is a minimalist Email server that does auto-activation. [more info here](KinanCity-mail/README.md) +- **KinanCity-core** is the core module that can also be used in command line. [more info here](KinanCity-core/README.md) +- **KinanCity-mail** is a minimalist Email server that does auto-activation. [more info here](KinanCity-mail/README.md) -## Why another tool ? +## Why another tool? -There are already many accout creator with each their specific features. +There are already many account creators with their specific features each. KinanCity was born by taking the best features of them to have a complete solution. -The advantages of KinanCity : -* is cross platform compatible (Unix, Windows, Mac) +The advantages of KinanCity: +* cross platform compatible (Unix, Windows, Mac) * can work on headless systems without any need of a web driver * does parallel processing to be faster * can use multiple proxies to go over the limit of 30 accounts per hour (5 accounts per 10 minutes) @@ -34,7 +34,7 @@ The advantages of KinanCity : You can **download** the latest builds [here](https://github.com/drallieiv/KinanCity/releases) or -You can **compile** it yourself using a **maven** goal : `mvn package` +You can **compile** it yourself using a **maven** goal: `mvn package` **KinanCity** modules are java applications and all you need is a Java 8 Runtime Environment on your system. From 0bf0f0551a761689a40f1f65bb22a71cc7676cfc Mon Sep 17 00:00:00 2001 From: Adrien Nguyen Date: Mon, 24 Aug 2020 00:34:18 +0200 Subject: [PATCH 02/17] Add email activation --- .../dependency-reduced-pom.xml | 2 +- KinanCity-captcha-server/pom.xml | 2 +- KinanCity-mail/pom.xml | 12 +- .../kinancity/mail/EmailChangeRequest.java | 7 + .../java/com/kinancity/mail/FileLogger.java | 5 +- .../com/kinancity/mail/KcMessageHandler.java | 72 ++++++++-- .../mail/KcMessageHandlerFactory.java | 10 +- .../kinancity/mail/MailServerApplication.java | 28 +++- .../mail/mailchanger/DirectEmailChanger.java | 136 ++++++++++++++++++ .../mail/mailchanger/EmailChanger.java | 7 + .../mailchanger/MailChangerException.java | 11 ++ .../mail/mailchanger/PasswordProvider.java | 5 + .../mailchanger/PasswordProviderFactory.java | 15 ++ .../mail/mailchanger/ToFileEmailChanger.java | 12 ++ .../impl/StaticPasswordProvider.java | 17 +++ result.csv | 13 ++ 16 files changed, 336 insertions(+), 18 deletions(-) create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/EmailChangeRequest.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/DirectEmailChanger.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/EmailChanger.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/MailChangerException.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/PasswordProvider.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/PasswordProviderFactory.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/ToFileEmailChanger.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/impl/StaticPasswordProvider.java create mode 100644 result.csv diff --git a/KinanCity-captcha-server/dependency-reduced-pom.xml b/KinanCity-captcha-server/dependency-reduced-pom.xml index 7df8271..1d3cee6 100644 --- a/KinanCity-captcha-server/dependency-reduced-pom.xml +++ b/KinanCity-captcha-server/dependency-reduced-pom.xml @@ -21,7 +21,7 @@ - com.kinancity.captcha.server.Application + com.kinancity.captcha.server.CaptchaServer META-INF/spring.handlers diff --git a/KinanCity-captcha-server/pom.xml b/KinanCity-captcha-server/pom.xml index 223abe9..3eab0b2 100644 --- a/KinanCity-captcha-server/pom.xml +++ b/KinanCity-captcha-server/pom.xml @@ -24,7 +24,7 @@ spring-boot-maven-plugin true - com.kinancity.captcha.server.Application + com.kinancity.captcha.server.CaptchaServer diff --git a/KinanCity-mail/pom.xml b/KinanCity-mail/pom.xml index 99b57ff..3f3ce1b 100644 --- a/KinanCity-mail/pom.xml +++ b/KinanCity-mail/pom.xml @@ -7,7 +7,7 @@ 1.4.2-SNAPSHOT KinanCity-mail - 1.5.4 + 2.0.1 Email validation for KinanCity @@ -58,8 +58,14 @@ 3.6.2 test - - + + org.jsoup + jsoup + 1.13.1 + compile + + + diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/EmailChangeRequest.java b/KinanCity-mail/src/main/java/com/kinancity/mail/EmailChangeRequest.java new file mode 100644 index 0000000..a8ee055 --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/EmailChangeRequest.java @@ -0,0 +1,7 @@ +package com.kinancity.mail; + +public class EmailChangeRequest extends Activation { + public EmailChangeRequest(String link, String email) { + super(link, email); + } +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/FileLogger.java b/KinanCity-mail/src/main/java/com/kinancity/mail/FileLogger.java index 3d7a270..a67325c 100644 --- a/KinanCity-mail/src/main/java/com/kinancity/mail/FileLogger.java +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/FileLogger.java @@ -1,5 +1,6 @@ package com.kinancity.mail; +import com.kinancity.mail.mailchanger.ToFileEmailChanger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,13 +11,15 @@ public class FileLogger { public static final String BAD = "BAD"; public static final String THROTTLED = "THROTTLED"; public static final String ERROR = "ERROR"; + public static final String EXPIRED = "EXPIRED"; public static final String SKIPPED = "SKIPPED"; private static Logger LOGGER = LoggerFactory.getLogger("LINKS"); public static void logStatus(Activation link, String status) { - LOGGER.info("{};{};{}", link.getLink(), link.getEmail(), status); + String type = (link instanceof EmailChangeRequest) ? "MAILCHANGE" : "ACTIVATION"; + LOGGER.info("{};{};{};{}", type, link.getLink(), link.getEmail(), status); } public static Activation fromLog(String line) { diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/KcMessageHandler.java b/KinanCity-mail/src/main/java/com/kinancity/mail/KcMessageHandler.java index 0e362b4..9a87897 100644 --- a/KinanCity-mail/src/main/java/com/kinancity/mail/KcMessageHandler.java +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/KcMessageHandler.java @@ -12,6 +12,7 @@ import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; +import com.kinancity.mail.mailchanger.EmailChanger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.subethamail.smtp.MessageHandler; @@ -32,6 +33,7 @@ public class KcMessageHandler implements MessageHandler { private static final String POKEMON_DOMAIN = "@pokemon.com"; private static final String ACTIVATION_EXP = "https://club.pokemon.com/us/pokemon-trainer-club/activated/.*"; + private static final String EMAIL_CHANGE_EXP = "https://club.pokemon.com/us/pokemon-trainer-club/email-change-approval/[a-zA-Z0-9]+"; private Logger logger = LoggerFactory.getLogger(getClass()); @@ -40,16 +42,29 @@ public class KcMessageHandler implements MessageHandler { private LinkActivator activator; + private EmailChanger emailChanger; + @Setter private boolean acceptAllFrom = false; + private boolean handleActivation = true; + + private boolean handleEmailChange = true; + + @Setter + private boolean logOtherMessages = true; + /** * Construct with a given Link Activator * * @param activator */ - public KcMessageHandler(LinkActivator activator) { + public KcMessageHandler(LinkActivator activator, EmailChanger emailChanger) { this.activator = activator; + this.emailChanger = emailChanger; + + handleActivation = activator != null; + handleEmailChange = emailChanger != null; } @Override @@ -82,17 +97,22 @@ public void data(InputStream data) throws RejectException, TooMuchDataException, BodyPart part = mpart.getBodyPart(0); String content = (String) part.getContent(); - Pattern p = Pattern.compile(ACTIVATION_EXP); - Matcher m = p.matcher(content); - if (m.find()) { - String activationLink = m.group(0); - logger.info("Activation link found for email {} : [{}]", this.recipient, activationLink); + boolean done = false; - // Link activation, may be sync or async - activator.activateLink(new Activation(activationLink, this.recipient)); + if(handleActivation) { + if(searchForActivationLink(content)) { + done = true; + } + } - } else { - logger.error("No activation link found"); + if(handleEmailChange && !done) { + if(searchForEmailChangeRequestLink(content)) { + done = true; + } + } + + if(logOtherMessages && !done) { + logger.info("Other unknown mail received with content : {}", content); } } catch (MessagingException e) { @@ -104,6 +124,38 @@ public void data(InputStream data) throws RejectException, TooMuchDataException, } + private boolean searchForActivationLink(String content) { + Pattern p = Pattern.compile(ACTIVATION_EXP); + Matcher m = p.matcher(content); + if (m.find()) { + String activationLink = m.group(0); + logger.info("Activation link found for email {} : [{}]", this.recipient, activationLink); + + // Link activation, may be sync or async + activator.activateLink(new Activation(activationLink, this.recipient)); + return true; + } else { + logger.error("No activation link found"); + return false; + } + } + + private boolean searchForEmailChangeRequestLink(String content) { + Pattern p = Pattern.compile(EMAIL_CHANGE_EXP); + Matcher m = p.matcher(content); + if (m.find()) { + String emailChangeLink = m.group(0); + logger.info("Email Change Request link found for email {} : [{}]", this.recipient, emailChangeLink); + + // Email Change acceptation, may be sync or async + emailChanger.acceptChange(new EmailChangeRequest(emailChangeLink, this.recipient)); + return true; + } else { + logger.error("No email change link found"); + return false; + } + } + @Override public void done() { diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/KcMessageHandlerFactory.java b/KinanCity-mail/src/main/java/com/kinancity/mail/KcMessageHandlerFactory.java index bab444e..f3cab3c 100644 --- a/KinanCity-mail/src/main/java/com/kinancity/mail/KcMessageHandlerFactory.java +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/KcMessageHandlerFactory.java @@ -1,5 +1,6 @@ package com.kinancity.mail; +import com.kinancity.mail.mailchanger.EmailChanger; import org.subethamail.smtp.MessageContext; import org.subethamail.smtp.MessageHandler; import org.subethamail.smtp.MessageHandlerFactory; @@ -18,6 +19,8 @@ public class KcMessageHandlerFactory implements MessageHandlerFactory { private LinkActivator activator; + private EmailChanger emailChanger; + @Setter private boolean acceptAllFrom = false; @@ -25,9 +28,14 @@ public KcMessageHandlerFactory(LinkActivator activator) { this.activator = activator; } + public KcMessageHandlerFactory(LinkActivator activator, EmailChanger emailChanger) { + this.activator = activator; + this.emailChanger = emailChanger; + } + @Override public MessageHandler create(MessageContext ctx) { - KcMessageHandler handler = new KcMessageHandler(activator); + KcMessageHandler handler = new KcMessageHandler(activator, emailChanger); handler.setAcceptAllFrom(acceptAllFrom); return handler; } diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/MailServerApplication.java b/KinanCity-mail/src/main/java/com/kinancity/mail/MailServerApplication.java index c1b7e5c..9edbc68 100644 --- a/KinanCity-mail/src/main/java/com/kinancity/mail/MailServerApplication.java +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/MailServerApplication.java @@ -10,6 +10,8 @@ import java.util.Properties; import java.util.regex.Pattern; +import com.kinancity.mail.mailchanger.*; +import com.kinancity.mail.mailchanger.impl.StaticPasswordProvider; import org.subethamail.wiser.Wiser; import com.kinancity.mail.activator.LinkActivator; @@ -73,11 +75,15 @@ public static void main(String[] args) { } else { LinkActivator activator = getQueueLinkActivator(); + EmailChanger emailChanger = null; + if (mode.equals("log")) { System.out.println("Started in Log Mode"); activator = new ToFileLinkActivator(); + emailChanger = new ToFileEmailChanger(); } else { System.out.println("Started in Direct Mode"); + emailChanger = getEmailChanger(); } activator.start(); @@ -86,11 +92,20 @@ public static void main(String[] args) { wiser.setPort(25); wiser.setHostname("localhost"); - KcMessageHandlerFactory handlerFactory = new KcMessageHandlerFactory(activator); + KcMessageHandlerFactory handlerFactory = new KcMessageHandlerFactory(activator, emailChanger); boolean disableDomainFilter = config.getProperty("disableDomainFilter", "false").equals("true"); if (disableDomainFilter) { handlerFactory.setAcceptAllFrom(true); } + + if(activator != null ) { + System.out.println("Email Activation is Enabled"); + } + + if(emailChanger != null) { + System.out.println("Email Change is Enabled"); + } + wiser.getServer().setMessageHandlerFactory(handlerFactory); wiser.start(); } @@ -113,6 +128,17 @@ public static void loadConfig() { } } + private static EmailChanger getEmailChanger() { + String isActive = config.getProperty("emailChanger.active"); + if(isActive == null || isActive.equals("true")) { + String mailPasswordConfig = config.getProperty("emailChanger.password"); + PasswordProvider passwordProvider = PasswordProviderFactory.getPasswordProvider(mailPasswordConfig); + return new DirectEmailChanger(passwordProvider); + } else { + return null; + } + } + public static LinkActivator getQueueLinkActivator() { QueueLinkActivator activator; diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/DirectEmailChanger.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/DirectEmailChanger.java new file mode 100644 index 0000000..2f7a594 --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/DirectEmailChanger.java @@ -0,0 +1,136 @@ +package com.kinancity.mail.mailchanger; + +import com.kinancity.mail.EmailChangeRequest; +import com.kinancity.mail.FileLogger; +import com.kinancity.mail.MailConstants; +import okhttp3.*; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.select.Elements; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +public class DirectEmailChanger implements EmailChanger{ + private Logger logger = LoggerFactory.getLogger(getClass()); + + private final String EXPIRED_TXT = "Your attempt to change your email address is invalid or has timed out"; + private final String REQUEST_TXT = "A request has been made to change your email address"; + private final String EMAIL_UPDATED = "You have updated your email address."; + + /** + * + */ + + private okhttp3.OkHttpClient client; + + private PasswordProvider passwordProvider; + + public DirectEmailChanger(PasswordProvider passwordProvider) { + this.client = new OkHttpClient.Builder().build(); + this.passwordProvider = passwordProvider; + } + + @Override + public boolean acceptChange(EmailChangeRequest emailChangeRequest) { + + try { + Request request = new Request.Builder() + .header(MailConstants.HEADER_USER_AGENT, MailConstants.CHROME_USER_AGENT) + .url(emailChangeRequest.getLink()) + .build(); + + Response response = client.newCall(request).execute(); + String strResponse = response.body().string(); + + if (response.isSuccessful()) { + // Now check the page itself + if (strResponse.contains(EXPIRED_TXT)) { + logger.info("Email Change Link Expired"); + FileLogger.logStatus(emailChangeRequest, FileLogger.EXPIRED); + return false; + } + + if (strResponse.contains(REQUEST_TXT)) { + logger.info("Email Change Link Valid"); + // Parse the response + Document doc = Jsoup.parse(response.body().string()); + response.body().close(); + // Grab all data + String crsfToken = this.getCrsfToken(doc); + String currentEmail = this.getField(doc, "current_email"); + String newEmail = this.getField(doc, "new_email"); + // Add the password + String password = passwordProvider.getPassword(currentEmail); + + // Send everything + FormBody body = new FormBody.Builder() + .add("secure-change-approve", "Confirm") + .add("csrfmiddlewaretoken", crsfToken) + .add("current_email", currentEmail) + .add("new_email", newEmail) + .add("current_password", password) + .build(); + Request acceptRequest = new Request.Builder() + .header(MailConstants.HEADER_USER_AGENT, MailConstants.CHROME_USER_AGENT) + .url(emailChangeRequest.getLink()) + .post(body) + .build(); + + Response acceptResponse = client.newCall(acceptRequest).execute(); + if (response.isSuccessful()) { + String strAcceptResponse = acceptResponse.body().string(); + + if (strAcceptResponse.contains(EMAIL_UPDATED)) { + logger.info("Email Change Successful"); + FileLogger.logStatus(emailChangeRequest, FileLogger.OK); + return true; + } else { + logger.info("Email Change FAILED"); + FileLogger.logStatus(emailChangeRequest, FileLogger.ERROR); + return false; + } + + + } else { + logger.error("Mail Change failed : Failed to call PTC to accept"); + FileLogger.logStatus(emailChangeRequest, FileLogger.ERROR); + return false; + } + } + + } else { + logger.error("Mail Change failed : Failed to call PTC"); + FileLogger.logStatus(emailChangeRequest, FileLogger.ERROR); + return false; + } + } catch (MailChangerException e) { + logger.error("Mail Change failed : {}", e.getMessage()); + FileLogger.logStatus(emailChangeRequest, FileLogger.ERROR); + return false; + } catch (IOException e) { + return false; + } + + return false; + } + + private String getCrsfToken(Document doc) throws MailChangerException { + Elements tokenField = doc.select("[name=csrfmiddlewaretoken]"); + if (tokenField.isEmpty()) { + throw new MailChangerException("CSRF Token not found"); + } else { + return tokenField.get(0).val(); + } + } + + private String getField(Document doc, String fieldName) throws MailChangerException { + Elements tokenField = doc.select("[name="+fieldName+"]"); + if (tokenField.isEmpty()) { + throw new MailChangerException(fieldName + " Field not found"); + } else { + return tokenField.get(0).val(); + } + } +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/EmailChanger.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/EmailChanger.java new file mode 100644 index 0000000..34d8400 --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/EmailChanger.java @@ -0,0 +1,7 @@ +package com.kinancity.mail.mailchanger; + +import com.kinancity.mail.EmailChangeRequest; + +public interface EmailChanger { + boolean acceptChange(EmailChangeRequest emailChangeRequest); +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/MailChangerException.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/MailChangerException.java new file mode 100644 index 0000000..a10ea4c --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/MailChangerException.java @@ -0,0 +1,11 @@ +package com.kinancity.mail.mailchanger; + +public class MailChangerException extends Exception{ + public MailChangerException(String message) { + super(message); + } + + public MailChangerException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/PasswordProvider.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/PasswordProvider.java new file mode 100644 index 0000000..8d9815d --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/PasswordProvider.java @@ -0,0 +1,5 @@ +package com.kinancity.mail.mailchanger; + +public interface PasswordProvider { + String getPassword(String currentEmail); +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/PasswordProviderFactory.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/PasswordProviderFactory.java new file mode 100644 index 0000000..d5e6f08 --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/PasswordProviderFactory.java @@ -0,0 +1,15 @@ +package com.kinancity.mail.mailchanger; + +import com.kinancity.mail.mailchanger.impl.StaticPasswordProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PasswordProviderFactory { + private static Logger LOGGER = LoggerFactory.getLogger(PasswordProviderFactory.class); + + public static PasswordProvider getPasswordProvider(String config){ + // For now only static + LOGGER.info("Using Static Password Provider"); + return new StaticPasswordProvider(config); + } +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/ToFileEmailChanger.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/ToFileEmailChanger.java new file mode 100644 index 0000000..ed21013 --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/ToFileEmailChanger.java @@ -0,0 +1,12 @@ +package com.kinancity.mail.mailchanger; + +import com.kinancity.mail.EmailChangeRequest; +import com.kinancity.mail.FileLogger; + +public class ToFileEmailChanger implements EmailChanger { + @Override + public boolean acceptChange(EmailChangeRequest emailChangeRequest) { + FileLogger.logStatus(emailChangeRequest, FileLogger.SKIPPED); + return true; + } +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/impl/StaticPasswordProvider.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/impl/StaticPasswordProvider.java new file mode 100644 index 0000000..8f92c16 --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/impl/StaticPasswordProvider.java @@ -0,0 +1,17 @@ +package com.kinancity.mail.mailchanger.impl; + +import com.kinancity.mail.mailchanger.PasswordProvider; + +public class StaticPasswordProvider implements PasswordProvider { + + private String fixedPwd; + + public StaticPasswordProvider(String fixedPwd) { + this.fixedPwd = fixedPwd; + } + + @Override + public String getPassword(String currentEmail) { + return fixedPwd; + } +} diff --git a/result.csv b/result.csv new file mode 100644 index 0000000..add765f --- /dev/null +++ b/result.csv @@ -0,0 +1,13 @@ +# Batch creation start at : 2020-08-23 14:41:55.949 +kinan0820dv0001;Kinan+0820!;kinan0820dv0001@dev.kinancity.ovh;1947-05-20;US;OK; +kinan0820dv0011;Kinan+0820!;kinan0820dv0011@dev.kinancity.ovh;1950-09-03;US;OK; +kinan0820dv0021;Kinan+0820!;kinan0820dv0021@dev.kinancity.ovh;1949-08-19;US;OK; +kinan0820dv0041;Kinan+0820!;kinan0820dv0041@dev.kinancity.ovh;1952-05-10;US;OK; +kinan0820dv0031;Kinan+0820!;kinan0820dv0031@dev.kinancity.ovh;1959-09-24;US;OK; +kinan0820dv0051;Kinan+0820!;kinan0820dv0051@dev.kinancity.ovh;1987-01-26;US;OK; +kinan0820dv0061;Kinan+0820!;kinan0820dv0061@dev.kinancity.ovh;1997-07-18;US;OK; +kinan0820dv0081;Kinan+0820!;kinan0820dv0081@dev.kinancity.ovh;1970-12-26;US;OK; +kinan0820dv0071;Kinan+0820!;kinan0820dv0071@dev.kinancity.ovh;1985-08-30;US;OK; +kinan0820dv0091;Kinan+0820!;kinan0820dv0091@dev.kinancity.ovh;1964-05-15;US;OK; +# Batch creation end at : 2020-08-23 15:20:34.6 +# 10 success, 0 errors From ad515e5da38414829115312a1865f4d30272be28 Mon Sep 17 00:00:00 2001 From: Adrien Nguyen Date: Sat, 5 Sep 2020 13:53:58 +0200 Subject: [PATCH 03/17] Email changer with password management --- KinanCity-mail/accounts.example.csv | 3 + KinanCity-mail/config.example.properties | 17 ++- KinanCity-mail/pom.xml | 2 +- .../java/com/kinancity/mail/Activation.java | 2 +- .../kinancity/mail/EmailChangeRequest.java | 5 + .../java/com/kinancity/mail/FileLogger.java | 19 +++- .../com/kinancity/mail/KcMessageHandler.java | 57 ++++++++-- .../mail/KcMessageHandlerFactory.java | 4 - .../kinancity/mail/MailServerApplication.java | 62 +++++++---- .../com/kinancity/mail/SaveAllCookieJar.java | 34 ++++++ .../kinancity/mail/SkippedFileProcessor.java | 17 +-- .../mail/config/CsvPasswordReader.java | 100 ++++++++++++++++++ .../mail/config/PasswordProviderFactory.java | 59 +++++++++++ .../mail/mailchanger/DirectEmailChanger.java | 24 ++++- .../mail/mailchanger/HybridActivator.java | 31 ++++++ .../mail/mailchanger/PasswordProvider.java | 5 - .../mailchanger/PasswordProviderFactory.java | 15 --- .../mailchanger/password/EmailMatcher.java | 5 + .../password/PasswordProvider.java | 11 ++ .../password/matcher/AnyEmailMatcher.java | 11 ++ .../password/matcher/ExactEmailMatcher.java | 20 ++++ .../password/matcher/MatchingPair.java | 12 +++ .../password/matcher/RegexEmailMatcher.java | 34 ++++++ .../provider/ChainedPasswordProvider.java | 28 +++++ .../provider/MappedPasswordProvider.java | 23 ++++ .../provider/MatcherPasswordProvider.java | 26 +++++ .../provider}/StaticPasswordProvider.java | 4 +- .../com/kinancity/mail/wiser/KinanWiser.java | 36 +++++++ .../config/PasswordProviderFactoryTest.java | 66 ++++++++++++ result.csv | 13 --- 30 files changed, 659 insertions(+), 86 deletions(-) create mode 100644 KinanCity-mail/accounts.example.csv create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/SaveAllCookieJar.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/config/CsvPasswordReader.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/config/PasswordProviderFactory.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/HybridActivator.java delete mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/PasswordProvider.java delete mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/PasswordProviderFactory.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/EmailMatcher.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/PasswordProvider.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/matcher/AnyEmailMatcher.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/matcher/ExactEmailMatcher.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/matcher/MatchingPair.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/matcher/RegexEmailMatcher.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/provider/ChainedPasswordProvider.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/provider/MappedPasswordProvider.java create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/provider/MatcherPasswordProvider.java rename KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/{impl => password/provider}/StaticPasswordProvider.java (70%) create mode 100644 KinanCity-mail/src/main/java/com/kinancity/mail/wiser/KinanWiser.java create mode 100644 KinanCity-mail/src/test/java/com/kinancity/mail/config/PasswordProviderFactoryTest.java delete mode 100644 result.csv diff --git a/KinanCity-mail/accounts.example.csv b/KinanCity-mail/accounts.example.csv new file mode 100644 index 0000000..a0f582f --- /dev/null +++ b/KinanCity-mail/accounts.example.csv @@ -0,0 +1,3 @@ +#username;email;password +username1;username1@myMxDomain.com;testAA123+ +username2;username2@myMxDomain.com;testAA456+ diff --git a/KinanCity-mail/config.example.properties b/KinanCity-mail/config.example.properties index c85afc3..8885bca 100644 --- a/KinanCity-mail/config.example.properties +++ b/KinanCity-mail/config.example.properties @@ -2,4 +2,19 @@ #proxy=http://login:pass@127.0.0.1:3128|http://login:pass@127.0.0.1:3128 # Uncomment the line below if you want to accept emails that does not come from pokemon.com -#disableDomainFilter=true \ No newline at end of file +#disableDomainFilter=true + +# Add your domain and uncomment the line below to only accept emails sent to a specific email address +#allowedDomains=myDomain.xyz +#hostname=myDomain.xyz + + +# Activate the email changer +# emailChanger.active=true + +# Loading from a csv file +# emailChanger.password.csv=accounts.example.csv +# Mapping between email account and password with wildcard +# emailChanger.password.mapping=aaaaa.*@domain.com:pass1||bbbbb.*@.*:pass2 +# Always the same password +# emailChanger.password.static=sameForAll \ No newline at end of file diff --git a/KinanCity-mail/pom.xml b/KinanCity-mail/pom.xml index 3f3ce1b..e06b969 100644 --- a/KinanCity-mail/pom.xml +++ b/KinanCity-mail/pom.xml @@ -7,7 +7,7 @@ 1.4.2-SNAPSHOT KinanCity-mail - 2.0.1 + 2.0.2 Email validation for KinanCity diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/Activation.java b/KinanCity-mail/src/main/java/com/kinancity/mail/Activation.java index aae75df..85c68c4 100644 --- a/KinanCity-mail/src/main/java/com/kinancity/mail/Activation.java +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/Activation.java @@ -7,7 +7,7 @@ public class Activation { private String link; private String email; - private String status; + private String status = "UNDEF"; public Activation(String link, String email) { this.link = link; diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/EmailChangeRequest.java b/KinanCity-mail/src/main/java/com/kinancity/mail/EmailChangeRequest.java index a8ee055..187a5d8 100644 --- a/KinanCity-mail/src/main/java/com/kinancity/mail/EmailChangeRequest.java +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/EmailChangeRequest.java @@ -1,7 +1,12 @@ package com.kinancity.mail; public class EmailChangeRequest extends Activation { + public EmailChangeRequest(String link, String email) { super(link, email); } + + public EmailChangeRequest(String link, String email, String status) { + super(link, email, status); + } } diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/FileLogger.java b/KinanCity-mail/src/main/java/com/kinancity/mail/FileLogger.java index a67325c..aa6e5a6 100644 --- a/KinanCity-mail/src/main/java/com/kinancity/mail/FileLogger.java +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/FileLogger.java @@ -15,19 +15,30 @@ public class FileLogger { public static final String SKIPPED = "SKIPPED"; + public static final String TYPE_MAILCHANGE = "MAILCHANGE"; + public static final String TYPE_ACTIVATION = "ACTIVATION"; + private static Logger LOGGER = LoggerFactory.getLogger("LINKS"); public static void logStatus(Activation link, String status) { - String type = (link instanceof EmailChangeRequest) ? "MAILCHANGE" : "ACTIVATION"; + String type = (link instanceof EmailChangeRequest) ? TYPE_MAILCHANGE : TYPE_ACTIVATION; LOGGER.info("{};{};{};{}", type, link.getLink(), link.getEmail(), status); } public static Activation fromLog(String line) { String[] parts = line.split(";"); - if (parts.length > 2) { - return new Activation(parts[0], parts[1], parts[2]); + if(parts[0].equals(TYPE_ACTIVATION)) { + if (parts.length > 3) { + return new Activation(parts[1], parts[2], parts[3]); + } else { + return new Activation(parts[1], null, parts[2]); + } } else { - return new Activation(parts[0], null, parts[1]); + if (parts.length > 3) { + return new EmailChangeRequest(parts[1], parts[2], parts[3]); + } else { + return new EmailChangeRequest(parts[1], parts[2]); + } } } } diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/KcMessageHandler.java b/KinanCity-mail/src/main/java/com/kinancity/mail/KcMessageHandler.java index 9a87897..c49cf58 100644 --- a/KinanCity-mail/src/main/java/com/kinancity/mail/KcMessageHandler.java +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/KcMessageHandler.java @@ -2,6 +2,8 @@ import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -17,7 +19,6 @@ import org.slf4j.LoggerFactory; import org.subethamail.smtp.MessageHandler; import org.subethamail.smtp.RejectException; -import org.subethamail.smtp.TooMuchDataException; import com.kinancity.mail.activator.LinkActivator; @@ -78,10 +79,10 @@ public void recipient(String recipient) throws RejectException { } /** - * Parse email, exctact activation link and call Link Activator + * Parse email, extract activation link and call Link Activator */ @Override - public void data(InputStream data) throws RejectException, TooMuchDataException, IOException { + public void data(InputStream data) throws RejectException, IOException { // Only accept pokemon mails if (from.endsWith(POKEMON_DOMAIN) || acceptAllFrom) { @@ -93,26 +94,27 @@ public void data(InputStream data) throws RejectException, TooMuchDataException, MimeMessage message = new MimeMessage(session, data); MimeMultipart mpart = (MimeMultipart) message.getContent(); + List parts = getAllMsgParts(mpart); - BodyPart part = mpart.getBodyPart(0); - String content = (String) part.getContent(); + String textMsg = getTextPart(parts); + String htmlMsg = getHtmlPart(parts); boolean done = false; if(handleActivation) { - if(searchForActivationLink(content)) { + if(searchForActivationLink(textMsg)) { done = true; } } if(handleEmailChange && !done) { - if(searchForEmailChangeRequestLink(content)) { + if(searchForEmailChangeRequestLink(htmlMsg)) { done = true; } } if(logOtherMessages && !done) { - logger.info("Other unknown mail received with content : {}", content); + logger.info("Other unknown mail received with content : {}", textMsg); } } catch (MessagingException e) { @@ -124,6 +126,15 @@ public void data(InputStream data) throws RejectException, TooMuchDataException, } + private List getAllMsgParts(MimeMultipart mpart) throws MessagingException { + List parts = new ArrayList<>(); + int nbParts = mpart.getCount(); + for (int i = 0 ; i < nbParts; i++) { + parts.add(mpart.getBodyPart(i)); + } + return parts; + } + private boolean searchForActivationLink(String content) { Pattern p = Pattern.compile(ACTIVATION_EXP); Matcher m = p.matcher(content); @@ -156,6 +167,36 @@ private boolean searchForEmailChangeRequestLink(String content) { } } + + private String getTextPart(List parts) throws IOException, MessagingException { + BodyPart part = parts.stream().filter(this::isTextPart).findFirst().orElse(null); + return (String) part.getContent(); + } + private String getHtmlPart(List parts) throws IOException, MessagingException { + BodyPart part = parts.stream().filter(this::isHtmlPart).findFirst().orElse(null); + return (String) part.getContent(); + } + + + private boolean isTextPart(BodyPart bodyPart) { + try { + return bodyPart.isMimeType("text/plain"); + } catch ( MessagingException e ) { + logger.error("Error extracting text part form Email"); + return false; + } + } + + private boolean isHtmlPart(BodyPart bodyPart) { + try { + return bodyPart.isMimeType("text/html"); + } catch ( MessagingException e ) { + logger.error("Error extracting html part form Email"); + return false; + } + } + + @Override public void done() { diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/KcMessageHandlerFactory.java b/KinanCity-mail/src/main/java/com/kinancity/mail/KcMessageHandlerFactory.java index f3cab3c..c50e554 100644 --- a/KinanCity-mail/src/main/java/com/kinancity/mail/KcMessageHandlerFactory.java +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/KcMessageHandlerFactory.java @@ -24,10 +24,6 @@ public class KcMessageHandlerFactory implements MessageHandlerFactory { @Setter private boolean acceptAllFrom = false; - public KcMessageHandlerFactory(LinkActivator activator) { - this.activator = activator; - } - public KcMessageHandlerFactory(LinkActivator activator, EmailChanger emailChanger) { this.activator = activator; this.emailChanger = emailChanger; diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/MailServerApplication.java b/KinanCity-mail/src/main/java/com/kinancity/mail/MailServerApplication.java index 9edbc68..41f67ba 100644 --- a/KinanCity-mail/src/main/java/com/kinancity/mail/MailServerApplication.java +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/MailServerApplication.java @@ -11,8 +11,10 @@ import java.util.regex.Pattern; import com.kinancity.mail.mailchanger.*; -import com.kinancity.mail.mailchanger.impl.StaticPasswordProvider; -import org.subethamail.wiser.Wiser; +import com.kinancity.mail.mailchanger.password.PasswordProvider; +import com.kinancity.mail.config.PasswordProviderFactory; +import com.kinancity.mail.wiser.KinanWiser; +import lombok.extern.slf4j.Slf4j; import com.kinancity.mail.activator.LinkActivator; import com.kinancity.mail.activator.MultiThreadQueueLinkActivator; @@ -22,6 +24,7 @@ import com.kinancity.mail.proxy.HttpProxy; import com.kinancity.mail.tester.ThrottleTester; +@Slf4j public class MailServerApplication { private static final String CONFIG_FILE = "config.properties"; @@ -39,7 +42,7 @@ public static void main(String[] args) { } if (mode.equals("test")) { - System.out.println("Start in Tester Mode"); + log.info("Start in Tester Mode"); ThrottleTester tester = new ThrottleTester(); @@ -68,29 +71,46 @@ public static void main(String[] args) { } LinkActivator activator = getQueueLinkActivator(); - activator.start(); + EmailChanger mailChanger = getEmailChanger(); + + HybridActivator hybrid = new HybridActivator(activator, mailChanger); + hybrid.start(); - System.out.println("Started in file Mode"); - new SkippedFileProcessor(activator, filePath).process(); + log.info("Started in file Mode"); + SkippedFileProcessor processor = new SkippedFileProcessor(hybrid, filePath); + processor.process(); } else { LinkActivator activator = getQueueLinkActivator(); EmailChanger emailChanger = null; if (mode.equals("log")) { - System.out.println("Started in Log Mode"); + log.info("Started in Log Mode"); activator = new ToFileLinkActivator(); emailChanger = new ToFileEmailChanger(); } else { - System.out.println("Started in Direct Mode"); + log.info("Started in Direct Mode"); emailChanger = getEmailChanger(); } activator.start(); // Start Wiser server - Wiser wiser = new Wiser(); - wiser.setPort(25); - wiser.setHostname("localhost"); + KinanWiser wiser = new KinanWiser(); + int port = Integer.parseInt(config.getProperty("port", "25")); + wiser.setPort(port); + wiser.setHostname(config.getProperty("hostname", "")); + + // Additional Setup + wiser.getServer().setSoftwareName("Kinan Mail Server"); + + + log.info("SMTP server started on port {}", port); + + String allowedDomains = config.getProperty("allowedDomains"); + if(allowedDomains != null) { + log.info("Only accept emails for " + allowedDomains); + wiser.setAllowedDomain(allowedDomains); + } KcMessageHandlerFactory handlerFactory = new KcMessageHandlerFactory(activator, emailChanger); boolean disableDomainFilter = config.getProperty("disableDomainFilter", "false").equals("true"); @@ -99,13 +119,14 @@ public static void main(String[] args) { } if(activator != null ) { - System.out.println("Email Activation is Enabled"); + log.info("Email Activation is Enabled"); } if(emailChanger != null) { - System.out.println("Email Change is Enabled"); + log.info("Email Change is Enabled"); } + wiser.getServer().setMessageHandlerFactory(handlerFactory); wiser.start(); } @@ -117,22 +138,21 @@ public static void loadConfig() { try { File configFile = new File(CONFIG_FILE); if (!configFile.exists()) { - System.out.println("Missing configuration file " + CONFIG_FILE); + log.info("Missing configuration file " + CONFIG_FILE); return; } InputStream in = new FileInputStream(configFile); config.load(in); in.close(); } catch (IOException e) { - System.out.println("Error loading configuration file " + CONFIG_FILE); + log.info("Error loading configuration file " + CONFIG_FILE); } } private static EmailChanger getEmailChanger() { String isActive = config.getProperty("emailChanger.active"); if(isActive == null || isActive.equals("true")) { - String mailPasswordConfig = config.getProperty("emailChanger.password"); - PasswordProvider passwordProvider = PasswordProviderFactory.getPasswordProvider(mailPasswordConfig); + PasswordProvider passwordProvider = PasswordProviderFactory.getPasswordProvider(config); return new DirectEmailChanger(passwordProvider); } else { return null; @@ -162,19 +182,19 @@ public static LinkActivator getQueueLinkActivator() { List proxies = new LinkedList(Arrays.asList(proxy.split(Pattern.quote("|")))); String initialProxy = proxies.get(0); HttpProxy httpProxy = HttpProxy.fromURI(initialProxy); - System.out.println("Using proxy " + httpProxy); + log.info("Using proxy " + httpProxy); activator.setHttpProxy(httpProxy); proxies.remove(0); for (String backupProxyStr : proxies) { HttpProxy backupProxy = HttpProxy.fromURI(backupProxyStr); httpProxy.getOtherProxies().add(backupProxy); - System.out.println("with backup proxy " + backupProxy); + log.info("with backup proxy " + backupProxy); } } else { HttpProxy httpProxy = HttpProxy.fromURI(proxy); - System.out.println("Using proxy " + httpProxy); + log.info("Using proxy " + httpProxy); activator.setHttpProxy(httpProxy); } } @@ -196,7 +216,7 @@ public static LinkActivator getQueueLinkActivator() { limiter.setLimiterPause(Integer.parseInt(pause)); } - System.out.println("Using limiter " + limiter); + log.info("Using limiter " + limiter); activator.setLimiter(limiter); } diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/SaveAllCookieJar.java b/KinanCity-mail/src/main/java/com/kinancity/mail/SaveAllCookieJar.java new file mode 100644 index 0000000..2f7799f --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/SaveAllCookieJar.java @@ -0,0 +1,34 @@ +package com.kinancity.mail; + +import okhttp3.Cookie; +import okhttp3.CookieJar; +import okhttp3.HttpUrl; + +import java.util.ArrayList; +import java.util.List; + +/** + * Basic cookie jar that mix all the cookies together + * + * @author drallieiv + * + */ +public class SaveAllCookieJar implements CookieJar { + + private List cookies = new ArrayList<>(); + + @Override + public void saveFromResponse(HttpUrl url, List cookies) { + this.cookies.addAll(cookies); + } + + @Override + public List loadForRequest(HttpUrl url) { + return cookies != null ? cookies : new ArrayList<>(); + } + + public List getCookies() { + return cookies; + } + +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/SkippedFileProcessor.java b/KinanCity-mail/src/main/java/com/kinancity/mail/SkippedFileProcessor.java index a15501f..7744ef1 100644 --- a/KinanCity-mail/src/main/java/com/kinancity/mail/SkippedFileProcessor.java +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/SkippedFileProcessor.java @@ -11,6 +11,7 @@ import com.kinancity.mail.activator.LinkActivator; import com.kinancity.mail.activator.QueueLinkActivator; +import com.kinancity.mail.mailchanger.EmailChanger; import lombok.Setter; import lombok.extern.slf4j.Slf4j; @@ -36,14 +37,16 @@ public void process() { String line; while ((line = reader.readLine()) != null) { - // CSV or not ? - if (line.contains(";")) { - Activation activation = FileLogger.fromLog(line); - if (processStatus.contains(activation.getStatus().toUpperCase())) { - activator.activateLink(activation); + if(!line.isEmpty()) { + // CSV or not ? + if (line.contains(";")) { + Activation activation = FileLogger.fromLog(line); + if (processStatus.contains(activation.getStatus().toUpperCase())) { + activator.activateLink(activation); + } + } else { + activator.activateLink(new Activation(line, "")); } - } else { - activator.activateLink(new Activation(line, "")); } } diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/config/CsvPasswordReader.java b/KinanCity-mail/src/main/java/com/kinancity/mail/config/CsvPasswordReader.java new file mode 100644 index 0000000..2dbc3d0 --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/config/CsvPasswordReader.java @@ -0,0 +1,100 @@ +package com.kinancity.mail.config; + +import com.kinancity.mail.mailchanger.password.EmailMatcher; +import com.kinancity.mail.mailchanger.password.matcher.ExactEmailMatcher; +import com.kinancity.mail.mailchanger.password.matcher.MatchingPair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.*; + +public class CsvPasswordReader { + private Logger logger = LoggerFactory.getLogger(getClass()); + + private static final String CSV_COMMENT_PREFIX = "#"; + + private static final String CSV_SPLITTER = ";"; + + public static final String EMAIL = "email"; + public static final String PASSWORD = "password"; + + public List load(String accountFileName) { + logger.info("Will load a list of accout to create from a csv file"); + + File accountFile = new File(accountFileName); + if (!accountFile.exists() || !accountFile.canRead()) { + logger.error("Cannot open file {}. Abort", accountFileName); + } + + try { + return loadFile(accountFile); + } catch (FileNotFoundException e) { + logger.error("Cannot open file {}. Abort", accountFileName); + } + return new ArrayList<>(); + } + + + + /** + * Load CSV file and build a list of Exact matchers + * @param accountFile + * @return + * @throws FileNotFoundException + */ + private List loadFile(File accountFile) throws FileNotFoundException { + List rules = new ArrayList<>(); + try (Scanner scanner = new Scanner(accountFile)) { + // Read first line that must be header + String firstline = scanner.nextLine(); + List headers = null; + if (firstline != null && firstline.startsWith(CSV_COMMENT_PREFIX)) { + headers = Arrays.asList(firstline.replace(CSV_COMMENT_PREFIX, "").split(CSV_SPLITTER)); + if (!headers.containsAll(Arrays.asList(PASSWORD, EMAIL))) { + logger.error("CSV file header is missing either password or email fields."); + } + } else { + logger.error("CSV file is missing header line."); + return rules; + } + + // Read all other lines + while (scanner.hasNext()) { + String line = scanner.nextLine(); + rules.add(buildMatcherFromCsv(line, headers)); + } + } + return rules; + } + + /** + * Create a matching pair from csv + * @param line + * @param headers + * @return MatchingPair + */ + public MatchingPair buildMatcherFromCsv(String line, List headers) { + // Parse csv into data map + Map fieldMap = new HashMap<>(); + List fields = Arrays.asList(line.split(CSV_SPLITTER)); + for (int i = 0; i < Math.min(fields.size(), headers.size()); i++) { + fieldMap.put(headers.get(i), fields.get(i)); + } + return buildMatcherFromDataFromMap(fieldMap); + } + + /** + * Create an MatchingPair given a set of fields + * + * @param fieldMap + * @return MatchingPair + */ + public MatchingPair buildMatcherFromDataFromMap(Map fieldMap) { + String email = fieldMap.get(EMAIL); + String password = fieldMap.get(PASSWORD); + EmailMatcher matcher = new ExactEmailMatcher(email); + return new MatchingPair(matcher, password); + } +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/config/PasswordProviderFactory.java b/KinanCity-mail/src/main/java/com/kinancity/mail/config/PasswordProviderFactory.java new file mode 100644 index 0000000..60b364d --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/config/PasswordProviderFactory.java @@ -0,0 +1,59 @@ +package com.kinancity.mail.config; + +import com.kinancity.mail.mailchanger.password.PasswordProvider; +import com.kinancity.mail.mailchanger.password.matcher.AnyEmailMatcher; +import com.kinancity.mail.mailchanger.password.matcher.MatchingPair; +import com.kinancity.mail.mailchanger.password.matcher.RegexEmailMatcher; +import com.kinancity.mail.mailchanger.password.provider.MatcherPasswordProvider; +import com.kinancity.mail.mailchanger.password.provider.StaticPasswordProvider; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +@Slf4j +public class PasswordProviderFactory { + private static Logger LOGGER = LoggerFactory.getLogger(PasswordProviderFactory.class); + + private static final String STATIC_CONFIG = "emailChanger.password.static"; + private static final String MAPPING_CONFIG = "emailChanger.password.mapping"; + private static final String CSV_CONFIG = "emailChanger.password.csv"; + + public static PasswordProvider getPasswordProvider(Properties config) { + List mapping = new ArrayList<>(); + + // CSV Mapping + String csvConfig = config.getProperty(CSV_CONFIG); + if(StringUtils.isNotEmpty(csvConfig)) { + CsvPasswordReader csvReader = new CsvPasswordReader(); + mapping.addAll(csvReader.load(csvConfig)); + } + + // Regexp Mapping + String mappingConfig = config.getProperty(MAPPING_CONFIG); + if(StringUtils.isNotEmpty(mappingConfig)) { + String[] configs = mappingConfig.split("\\|\\|"); + for (String sConfig : configs) { + if(!sConfig.contains(":")){ + log.error("Invalid mapping config [{}] => skip", sConfig); + } else { + String[] data = sConfig.split(":"); + mapping.add(new MatchingPair(new RegexEmailMatcher(data[0]), data[1])); + } + } + + } + + // Default static Mapping + String staticConfig = config.getProperty(STATIC_CONFIG); + if(StringUtils.isNotEmpty(staticConfig)) { + mapping.add(new MatchingPair(new AnyEmailMatcher(), staticConfig)); + } + + MatcherPasswordProvider provider = new MatcherPasswordProvider(); + provider.setMatchingPairs(mapping); + return provider; + } +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/DirectEmailChanger.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/DirectEmailChanger.java index 2f7a594..aaedf82 100644 --- a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/DirectEmailChanger.java +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/DirectEmailChanger.java @@ -3,6 +3,8 @@ import com.kinancity.mail.EmailChangeRequest; import com.kinancity.mail.FileLogger; import com.kinancity.mail.MailConstants; +import com.kinancity.mail.SaveAllCookieJar; +import com.kinancity.mail.mailchanger.password.PasswordProvider; import okhttp3.*; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; @@ -27,8 +29,11 @@ public class DirectEmailChanger implements EmailChanger{ private PasswordProvider passwordProvider; + private SaveAllCookieJar cookieJar; + public DirectEmailChanger(PasswordProvider passwordProvider) { - this.client = new OkHttpClient.Builder().build(); + cookieJar = new SaveAllCookieJar(); + this.client = new OkHttpClient.Builder().cookieJar(cookieJar).build(); this.passwordProvider = passwordProvider; } @@ -43,6 +48,7 @@ public boolean acceptChange(EmailChangeRequest emailChangeRequest) { Response response = client.newCall(request).execute(); String strResponse = response.body().string(); + response.body().close(); if (response.isSuccessful()) { // Now check the page itself @@ -55,14 +61,19 @@ public boolean acceptChange(EmailChangeRequest emailChangeRequest) { if (strResponse.contains(REQUEST_TXT)) { logger.info("Email Change Link Valid"); // Parse the response - Document doc = Jsoup.parse(response.body().string()); - response.body().close(); + Document doc = Jsoup.parse(strResponse); + // Grab all data String crsfToken = this.getCrsfToken(doc); String currentEmail = this.getField(doc, "current_email"); String newEmail = this.getField(doc, "new_email"); // Add the password String password = passwordProvider.getPassword(currentEmail); + if(password == null) { + logger.error("Mail Change failed : missing password for email {}", currentEmail); + FileLogger.logStatus(emailChangeRequest, FileLogger.ERROR); + return false; + } // Send everything FormBody body = new FormBody.Builder() @@ -72,13 +83,18 @@ public boolean acceptChange(EmailChangeRequest emailChangeRequest) { .add("new_email", newEmail) .add("current_password", password) .build(); + Request acceptRequest = new Request.Builder() .header(MailConstants.HEADER_USER_AGENT, MailConstants.CHROME_USER_AGENT) + .header("Origin", "https://club.pokemon.com") + .header("referer", emailChangeRequest.getLink()) .url(emailChangeRequest.getLink()) .post(body) .build(); Response acceptResponse = client.newCall(acceptRequest).execute(); + cookieJar.getCookies().clear(); + if (response.isSuccessful()) { String strAcceptResponse = acceptResponse.body().string(); @@ -92,7 +108,6 @@ public boolean acceptChange(EmailChangeRequest emailChangeRequest) { return false; } - } else { logger.error("Mail Change failed : Failed to call PTC to accept"); FileLogger.logStatus(emailChangeRequest, FileLogger.ERROR); @@ -133,4 +148,5 @@ private String getField(Document doc, String fieldName) throws MailChangerExcept return tokenField.get(0).val(); } } + } diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/HybridActivator.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/HybridActivator.java new file mode 100644 index 0000000..5ac2c42 --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/HybridActivator.java @@ -0,0 +1,31 @@ +package com.kinancity.mail.mailchanger; + +import com.kinancity.mail.Activation; +import com.kinancity.mail.EmailChangeRequest; +import com.kinancity.mail.activator.LinkActivator; + +public class HybridActivator implements LinkActivator { + + private LinkActivator activator; + + private EmailChanger emailChanger; + + public HybridActivator(LinkActivator activator, EmailChanger emailChanger) { + this.activator = activator; + this.emailChanger = emailChanger; + } + + @Override + public boolean activateLink(Activation link) { + if(link instanceof EmailChangeRequest){ + return emailChanger.acceptChange((EmailChangeRequest) link); + } else { + return activator.activateLink(link); + } + } + + @Override + public void start() { + this.activator.start(); + } +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/PasswordProvider.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/PasswordProvider.java deleted file mode 100644 index 8d9815d..0000000 --- a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/PasswordProvider.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.kinancity.mail.mailchanger; - -public interface PasswordProvider { - String getPassword(String currentEmail); -} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/PasswordProviderFactory.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/PasswordProviderFactory.java deleted file mode 100644 index d5e6f08..0000000 --- a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/PasswordProviderFactory.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.kinancity.mail.mailchanger; - -import com.kinancity.mail.mailchanger.impl.StaticPasswordProvider; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class PasswordProviderFactory { - private static Logger LOGGER = LoggerFactory.getLogger(PasswordProviderFactory.class); - - public static PasswordProvider getPasswordProvider(String config){ - // For now only static - LOGGER.info("Using Static Password Provider"); - return new StaticPasswordProvider(config); - } -} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/EmailMatcher.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/EmailMatcher.java new file mode 100644 index 0000000..841a117 --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/EmailMatcher.java @@ -0,0 +1,5 @@ +package com.kinancity.mail.mailchanger.password; + +public interface EmailMatcher { + boolean matches(String email); +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/PasswordProvider.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/PasswordProvider.java new file mode 100644 index 0000000..2a796ca --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/PasswordProvider.java @@ -0,0 +1,11 @@ +package com.kinancity.mail.mailchanger.password; + +public interface PasswordProvider { + /** + * Return the password for that email. + * + * @param email email address + * @return the password or null if not handled + */ + String getPassword(String email); +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/matcher/AnyEmailMatcher.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/matcher/AnyEmailMatcher.java new file mode 100644 index 0000000..12bbe2f --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/matcher/AnyEmailMatcher.java @@ -0,0 +1,11 @@ +package com.kinancity.mail.mailchanger.password.matcher; + +import com.kinancity.mail.mailchanger.password.EmailMatcher; + +public class AnyEmailMatcher implements EmailMatcher { + + @Override + public boolean matches(String email) { + return true; + } +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/matcher/ExactEmailMatcher.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/matcher/ExactEmailMatcher.java new file mode 100644 index 0000000..f8ed89e --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/matcher/ExactEmailMatcher.java @@ -0,0 +1,20 @@ +package com.kinancity.mail.mailchanger.password.matcher; + +import com.kinancity.mail.mailchanger.password.EmailMatcher; + +public class ExactEmailMatcher implements EmailMatcher { + + private String email; + + public ExactEmailMatcher(String email) { + this.email = email; + } + + @Override + public boolean matches(String otherEmail) { + if(otherEmail == null || email == null){ + return false; + } + return otherEmail.equals(email); + } +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/matcher/MatchingPair.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/matcher/MatchingPair.java new file mode 100644 index 0000000..8c3adb8 --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/matcher/MatchingPair.java @@ -0,0 +1,12 @@ +package com.kinancity.mail.mailchanger.password.matcher; + +import com.kinancity.mail.mailchanger.password.EmailMatcher; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class MatchingPair { + private EmailMatcher matcher; + private String password; +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/matcher/RegexEmailMatcher.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/matcher/RegexEmailMatcher.java new file mode 100644 index 0000000..4ae45f7 --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/matcher/RegexEmailMatcher.java @@ -0,0 +1,34 @@ +package com.kinancity.mail.mailchanger.password.matcher; + +import com.kinancity.mail.mailchanger.password.EmailMatcher; +import lombok.Getter; + +import java.util.regex.Pattern; + +/** + * Matcher based on regular expression + */ +@Getter +public class RegexEmailMatcher implements EmailMatcher { + + private String regex; + + private Pattern exp; + + public RegexEmailMatcher(String regex) { + this.setRegex(regex); + } + + @Override + public boolean matches(String accountName) { + if(accountName == null){ + return false; + } + return exp.matcher(accountName).matches(); + } + + public void setRegex(String regex) { + this.regex = regex; + exp = Pattern.compile(regex); + } +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/provider/ChainedPasswordProvider.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/provider/ChainedPasswordProvider.java new file mode 100644 index 0000000..a94bb58 --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/provider/ChainedPasswordProvider.java @@ -0,0 +1,28 @@ +package com.kinancity.mail.mailchanger.password.provider; + +import com.kinancity.mail.mailchanger.password.PasswordProvider; +import org.apache.commons.lang.StringUtils; + +import java.util.List; + +public class ChainedPasswordProvider implements PasswordProvider { + + private List providerChain; + + public ChainedPasswordProvider(List providerChain) { + this.providerChain = providerChain; + } + + @Override + public String getPassword(String currentEmail) { + + for (PasswordProvider passwordProvider : providerChain) { + String password = passwordProvider.getPassword(currentEmail); + if(StringUtils.isNotEmpty(password)) { + return password; + } + } + + return null; + } +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/provider/MappedPasswordProvider.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/provider/MappedPasswordProvider.java new file mode 100644 index 0000000..75291f9 --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/provider/MappedPasswordProvider.java @@ -0,0 +1,23 @@ +package com.kinancity.mail.mailchanger.password.provider; + +import com.kinancity.mail.mailchanger.password.PasswordProvider; +import lombok.Getter; +import lombok.Setter; + +import java.util.Map; + +/** + * Fixed 1 to 1 mapping + */ +public class MappedPasswordProvider implements PasswordProvider { + + @Setter + @Getter + private Map mapping; + + + @Override + public String getPassword(String currentEmail) { + return mapping.get(currentEmail); + } +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/provider/MatcherPasswordProvider.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/provider/MatcherPasswordProvider.java new file mode 100644 index 0000000..4f6bb61 --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/provider/MatcherPasswordProvider.java @@ -0,0 +1,26 @@ +package com.kinancity.mail.mailchanger.password.provider; + +import com.kinancity.mail.mailchanger.password.PasswordProvider; +import com.kinancity.mail.mailchanger.password.matcher.MatchingPair; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +public class MatcherPasswordProvider implements PasswordProvider { + + @Setter + @Getter + private List matchingPairs; + + + @Override + public String getPassword(String email) { + for (MatchingPair pair : matchingPairs) { + if(pair.getMatcher().matches(email)) { + return pair.getPassword(); + } + } + return null; + } +} diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/impl/StaticPasswordProvider.java b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/provider/StaticPasswordProvider.java similarity index 70% rename from KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/impl/StaticPasswordProvider.java rename to KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/provider/StaticPasswordProvider.java index 8f92c16..98fe58f 100644 --- a/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/impl/StaticPasswordProvider.java +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/mailchanger/password/provider/StaticPasswordProvider.java @@ -1,6 +1,6 @@ -package com.kinancity.mail.mailchanger.impl; +package com.kinancity.mail.mailchanger.password.provider; -import com.kinancity.mail.mailchanger.PasswordProvider; +import com.kinancity.mail.mailchanger.password.PasswordProvider; public class StaticPasswordProvider implements PasswordProvider { diff --git a/KinanCity-mail/src/main/java/com/kinancity/mail/wiser/KinanWiser.java b/KinanCity-mail/src/main/java/com/kinancity/mail/wiser/KinanWiser.java new file mode 100644 index 0000000..4b86c9f --- /dev/null +++ b/KinanCity-mail/src/main/java/com/kinancity/mail/wiser/KinanWiser.java @@ -0,0 +1,36 @@ +package com.kinancity.mail.wiser; + +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.subethamail.wiser.Wiser; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +// Only Accept emails to a given domain +@Slf4j +public class KinanWiser extends Wiser { + + @Setter + private List allowedDomains = new ArrayList<>(); + + @Override + public boolean accept(String from, String recipient) { + boolean okay = super.accept(from, recipient); + + if(okay && !allowedDomains.isEmpty()) { + okay = allowedDomains.stream().anyMatch((allowedDomain) -> recipient.endsWith(allowedDomain) ); + } + + if(!okay) { + log.warn("Rejected email, from {} to {}", from, recipient); + } + + return okay; + } + + public void setAllowedDomain(String domains){ + this.allowedDomains = Arrays.asList(domains.split(",")); + } +} diff --git a/KinanCity-mail/src/test/java/com/kinancity/mail/config/PasswordProviderFactoryTest.java b/KinanCity-mail/src/test/java/com/kinancity/mail/config/PasswordProviderFactoryTest.java new file mode 100644 index 0000000..17159af --- /dev/null +++ b/KinanCity-mail/src/test/java/com/kinancity/mail/config/PasswordProviderFactoryTest.java @@ -0,0 +1,66 @@ +package com.kinancity.mail.config; + +import com.kinancity.mail.mailchanger.password.PasswordProvider; +import org.junit.Test; + +import java.util.Properties; + +import static org.assertj.core.api.Assertions.assertThat; + + +public class PasswordProviderFactoryTest { + + @Test + public void getStaticPasswordProvider() { + Properties config = new Properties(); + config.setProperty("emailChanger.password.static", "static"); + PasswordProvider factory = PasswordProviderFactory.getPasswordProvider(config); + + assertThat(factory.getPassword("anything")).isEqualTo("static"); + } + + @Test + public void getCsvPasswordProvider() { + Properties config = new Properties(); + config.setProperty("emailChanger.password.csv", "accounts.example.csv"); + PasswordProvider factory = PasswordProviderFactory.getPasswordProvider(config); + + assertThat(factory.getPassword("username1@myMxDomain.com")).isEqualTo("testAA123+"); + assertThat(factory.getPassword("username2@myMxDomain.com")).isEqualTo("testAA456+"); + assertThat(factory.getPassword("anything")).isNull(); + } + + @Test + public void getRegexPasswordProvider() { + Properties config = new Properties(); + config.setProperty("emailChanger.password.mapping", "aaaaa.*@domain.com:pass1||bbbbb[0-9]+@.*:pass2"); + PasswordProvider factory = PasswordProviderFactory.getPasswordProvider(config); + + assertThat(factory.getPassword("aaaaa123@domain.com")).isEqualTo("pass1"); + assertThat(factory.getPassword("aaaaaXYZ@domain.com")).isEqualTo("pass1"); + assertThat(factory.getPassword("bbbbb123@other.com")).isEqualTo("pass2"); + assertThat(factory.getPassword("anything")).isNull(); + assertThat(factory.getPassword("aaaaa123@other.com")).isNull(); + assertThat(factory.getPassword("bbbbbXYZ@other.com")).isNull(); + } + + @Test + public void getFullPasswordProvider() { + Properties config = new Properties(); + config.setProperty("emailChanger.password.csv", "accounts.example.csv"); + config.setProperty("emailChanger.password.mapping", "aaaaa.*@domain.com:pass1||bbbbb[0-9]+@.*:pass2"); + config.setProperty("emailChanger.password.static", "static"); + PasswordProvider factory = PasswordProviderFactory.getPasswordProvider(config); + + assertThat(factory.getPassword("username1@myMxDomain.com")).isEqualTo("testAA123+"); + assertThat(factory.getPassword("username2@myMxDomain.com")).isEqualTo("testAA456+"); + + assertThat(factory.getPassword("aaaaa123@domain.com")).isEqualTo("pass1"); + assertThat(factory.getPassword("aaaaaXYZ@domain.com")).isEqualTo("pass1"); + assertThat(factory.getPassword("bbbbb123@other.com")).isEqualTo("pass2"); + + assertThat(factory.getPassword("anything")).isEqualTo("static"); + assertThat(factory.getPassword("aaaaa123@other.com")).isEqualTo("static"); + assertThat(factory.getPassword("bbbbbXYZ@other.com")).isEqualTo("static"); + } +} \ No newline at end of file diff --git a/result.csv b/result.csv deleted file mode 100644 index add765f..0000000 --- a/result.csv +++ /dev/null @@ -1,13 +0,0 @@ -# Batch creation start at : 2020-08-23 14:41:55.949 -kinan0820dv0001;Kinan+0820!;kinan0820dv0001@dev.kinancity.ovh;1947-05-20;US;OK; -kinan0820dv0011;Kinan+0820!;kinan0820dv0011@dev.kinancity.ovh;1950-09-03;US;OK; -kinan0820dv0021;Kinan+0820!;kinan0820dv0021@dev.kinancity.ovh;1949-08-19;US;OK; -kinan0820dv0041;Kinan+0820!;kinan0820dv0041@dev.kinancity.ovh;1952-05-10;US;OK; -kinan0820dv0031;Kinan+0820!;kinan0820dv0031@dev.kinancity.ovh;1959-09-24;US;OK; -kinan0820dv0051;Kinan+0820!;kinan0820dv0051@dev.kinancity.ovh;1987-01-26;US;OK; -kinan0820dv0061;Kinan+0820!;kinan0820dv0061@dev.kinancity.ovh;1997-07-18;US;OK; -kinan0820dv0081;Kinan+0820!;kinan0820dv0081@dev.kinancity.ovh;1970-12-26;US;OK; -kinan0820dv0071;Kinan+0820!;kinan0820dv0071@dev.kinancity.ovh;1985-08-30;US;OK; -kinan0820dv0091;Kinan+0820!;kinan0820dv0091@dev.kinancity.ovh;1964-05-15;US;OK; -# Batch creation end at : 2020-08-23 15:20:34.6 -# 10 success, 0 errors From 47e4f0e5650daedfd440c157a9ecc922dee09880 Mon Sep 17 00:00:00 2001 From: Adrien Nguyen Date: Sun, 20 Sep 2020 00:10:47 +0200 Subject: [PATCH 04/17] Add Documentation --- KinanCity-core/README.md | 19 +++++++++++++++++++ KinanCity-mail/README.md | 22 ++++++++++++++++++++++ README.md | 18 +++++++++++++++++- docs/0_PTCsignup.png | Bin 0 -> 20765 bytes docs/1_KinanCore.png | Bin 0 -> 17010 bytes docs/2_IPrestrictions.png | Bin 0 -> 13547 bytes docs/3_email.png | Bin 0 -> 20966 bytes docs/4_kinanMail.png | Bin 0 -> 18992 bytes 8 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 docs/0_PTCsignup.png create mode 100644 docs/1_KinanCore.png create mode 100644 docs/2_IPrestrictions.png create mode 100644 docs/3_email.png create mode 100644 docs/4_kinanMail.png diff --git a/KinanCity-core/README.md b/KinanCity-core/README.md index 97c1873..06b85b9 100644 --- a/KinanCity-core/README.md +++ b/KinanCity-core/README.md @@ -2,6 +2,7 @@ **TABLE OF CONTENTS** +- [What does Kinan Core do ?](#what-does-kinan-core-do) - [Main usage and tips](#main-usage-and-tips) - Configuration examples - [Create a sequence of accounts](#create-a-sequence-of-accounts) @@ -9,6 +10,24 @@ - [Create a single account](#create-a-single-account) - [Additional parameters](#additional-parameters) +# What does Kinan Core do ? + +Instead of manually going to the PTC website, Kinan Core automates all the requests needed to create the account itself. + +The captcha challenge still needs to be done, but you can use a third party provider (see below) or implement your own (see kinanCity-captcha-server). + +![](../docs/1_KinanCore.png) + +In the end you will still need to take care of the activation link that will be sent by email. + +Instead of doing these steps one by one, Kinan core does multi-thread the process (see -thread option below). + +However PTC has some restrictions that only allows to create 5 accounts per 15 minutes from the same IP. + +![](../docs/2_IPrestrictions.png) + +To overcome that limit, Kinan Core includes an embedded cooldown system, and allows using multiple proxies to call PTC from several different IP addresses. + # Main usage and tips KinanCity-core main class accepts configuration from : diff --git a/KinanCity-mail/README.md b/KinanCity-mail/README.md index 8d87310..8b084de 100644 --- a/KinanCity-mail/README.md +++ b/KinanCity-mail/README.md @@ -1,13 +1,35 @@ # KinanCity-mail : Email Activator +## About activation emails + +Here is how activations emails work in a standard way : + +There are 2 part in an email address : after the @ symbol, is the internet domain where the email should be sent. And before the @ symbol is the username who this email should be delivered to. + +![](../docs/3_email.png) + +PTC sends and email to the address you gave it. +It arrives to the DNS server of that domain, that defines to which server the the emails should be sent. + +On that email server, there is a program setup with mailboxes for each user and the emails are stored. + +Then the user connects to that mailbox and retreives his emails. He will then find the email with the activation link and open it in his browser. + ## What this module does +If you have your own domain, you can run your own email server. +Instead of running a standard email server, you can run Kinan Mail instead. + This module can be run as a standalone service and will : - Start a **Mail server** listening to default port 25 - For each mail received from nintendo, the **activation link** is grabbed - A web request is made to the **activation** link +![](../docs/4_kinanMail.png) + +Using Kinan Mail, you don't need to create a mailbox for each account before hand. And the activation links are taken care automatically. + ## How to setup The machine running the email server must be accessible from **the internet**. diff --git a/README.md b/README.md index c4a373a..94dbbfd 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,25 @@ It is known as **Kiloude City** is the English version and **Batisques** in Fren Kinan City is known for it **Friend Safari** where many trainer comes to find pokemon. This is a good place if you want to meet a **lot of new trainers**. +## How does one get a Pokemon Trainer Club account ? + +In order to get a PTC account you need to go the pokemon website and request to create a new account, and complete theses steps. + +![](docs/0_PTCsignup.png) + +You then first have to fill some personal informations such as your country and birthdate. Then information about the account you want. + +You also need to complete a Captcha task to prove that you are a human. + +One the subscription is done, the account still needs to be activated in the next 48 hours. An email will be sent to the given address with a link. You need to follow that link to activate the account. + +NOTE : You cannot use the same email twice for multiple PTC account, they each need their own email address. + +Theses steps must be done for each PTC account you want to create. + ## What does KinanCity do ? -**KinanCity** is a tool that automates the creation of Pokemon Trainer Accounts and contains multiple modules. +**KinanCity** is a tool that automates the creation of Pokemon Trainer Accounts and contains multiple modules when you need to create a lot of accounts, fast. - **KinanCity-core** : is the core module that can also be used in command line. [more info here](KinanCity-core/README.md) - **KinanCity-mail** : is a minimalist Email server that does auto-activation. [more info here](KinanCity-mail/README.md) diff --git a/docs/0_PTCsignup.png b/docs/0_PTCsignup.png new file mode 100644 index 0000000000000000000000000000000000000000..44590137887ec4918993072d77463c4e552139a8 GIT binary patch literal 20765 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 Xv4HC$&(y$c4`G((#|}}kt}%ZB&EB|4 literal 0 HcmV?d00001 diff --git a/docs/1_KinanCore.png b/docs/1_KinanCore.png new file mode 100644 index 0000000000000000000000000000000000000000..a098ce8e545d4d0b0d1690083b6289134fa4fc8f GIT binary patch literal 17010 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 VN!h5M!T%+Zr;_Vr>);`y|1YyyCF=kH literal 0 HcmV?d00001 diff --git a/docs/2_IPrestrictions.png b/docs/2_IPrestrictions.png new file mode 100644 index 0000000000000000000000000000000000000000..59f6f7c1d213d117e9c45c04ccdc8d9469f204af GIT binary patch literal 13547 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 literal 0 HcmV?d00001 diff --git a/docs/3_email.png b/docs/3_email.png new file mode 100644 index 0000000000000000000000000000000000000000..ba1c8fdc4b05dd9bae9c5ecd5ba7a127fee9bdb3 GIT binary patch literal 20966 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%0DKvu}9rMa}>fP)(c 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 literal 0 HcmV?d00001 From dcb29cc8f2019f08093b1891ff78fc2df0e190bb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Apr 2021 17:44:39 +0000 Subject: [PATCH 05/17] build(deps): bump commons-io from 2.5 to 2.7 in /ptc-api Bumps commons-io from 2.5 to 2.7. Signed-off-by: dependabot[bot] --- ptc-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ptc-api/pom.xml b/ptc-api/pom.xml index f30441a..330f771 100644 --- a/ptc-api/pom.xml +++ b/ptc-api/pom.xml @@ -42,7 +42,7 @@ commons-io commons-io - 2.5 + 2.7 commons-lang From 17f84d0f8e8f4aba465f27919f483ef571c33679 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Apr 2021 17:36:04 +0000 Subject: [PATCH 06/17] build(deps): bump commons-io from 2.5 to 2.7 in /KinanCity-core Bumps commons-io from 2.5 to 2.7. Signed-off-by: dependabot[bot] --- KinanCity-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KinanCity-core/pom.xml b/KinanCity-core/pom.xml index ea7d46d..4fa8a3a 100644 --- a/KinanCity-core/pom.xml +++ b/KinanCity-core/pom.xml @@ -78,7 +78,7 @@ commons-io commons-io - 2.5 + 2.7 commons-lang From 157a12b57641911e6441fb4ce1c35888a3b2a6d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 04:37:33 +0000 Subject: [PATCH 07/17] build(deps-dev): bump junit from 4.12 to 4.13.1 in /KinanCity-utils Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1) Signed-off-by: dependabot[bot] --- KinanCity-utils/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KinanCity-utils/pom.xml b/KinanCity-utils/pom.xml index d4cb4d0..e688885 100644 --- a/KinanCity-utils/pom.xml +++ b/KinanCity-utils/pom.xml @@ -20,7 +20,7 @@ junit junit - 4.12 + 4.13.1 test From 5b5c91153da08e464fc6076650666e2b6c92c6da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Apr 2021 17:43:40 +0000 Subject: [PATCH 08/17] build(deps): bump commons-io from 2.5 to 2.7 in /KinanCity-mail Bumps commons-io from 2.5 to 2.7. Signed-off-by: dependabot[bot] --- KinanCity-mail/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KinanCity-mail/pom.xml b/KinanCity-mail/pom.xml index e06b969..ad202f6 100644 --- a/KinanCity-mail/pom.xml +++ b/KinanCity-mail/pom.xml @@ -22,7 +22,7 @@ commons-io commons-io - 2.5 + 2.7 From 3611479e12487c8c814cd17f0a172165c7850357 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 04:36:34 +0000 Subject: [PATCH 09/17] build(deps-dev): bump junit from 4.12 to 4.13.1 in /ptc-api Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1) Signed-off-by: dependabot[bot] --- ptc-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ptc-api/pom.xml b/ptc-api/pom.xml index 330f771..949eb48 100644 --- a/ptc-api/pom.xml +++ b/ptc-api/pom.xml @@ -69,7 +69,7 @@ junit junit - 4.12 + 4.13.1 test From 587b7a97388d724ec35ce273de878e07b1151a99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 03:34:18 +0000 Subject: [PATCH 10/17] build(deps-dev): bump junit from 4.12 to 4.13.1 in /KinanCity-core Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1) Signed-off-by: dependabot[bot] --- KinanCity-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KinanCity-core/pom.xml b/KinanCity-core/pom.xml index 4fa8a3a..b050598 100644 --- a/KinanCity-core/pom.xml +++ b/KinanCity-core/pom.xml @@ -102,7 +102,7 @@ junit junit - 4.12 + 4.13.1 test From 481f6918e95262f44c1ebe1b25a9339cbd8ca35f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 03:35:02 +0000 Subject: [PATCH 11/17] build(deps-dev): bump junit in /KinanCity-captcha-ImageTypers Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1) Signed-off-by: dependabot[bot] --- KinanCity-captcha-ImageTypers/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KinanCity-captcha-ImageTypers/pom.xml b/KinanCity-captcha-ImageTypers/pom.xml index b5df582..adbf406 100644 --- a/KinanCity-captcha-ImageTypers/pom.xml +++ b/KinanCity-captcha-ImageTypers/pom.xml @@ -27,7 +27,7 @@ junit junit - 4.12 + 4.13.1 test From 826e03cd6cafaeb67650dfebca9fccc6b637a8b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 03:39:24 +0000 Subject: [PATCH 12/17] build(deps-dev): bump junit in /KinanCity-captcha-api Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1) Signed-off-by: dependabot[bot] --- KinanCity-captcha-api/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KinanCity-captcha-api/pom.xml b/KinanCity-captcha-api/pom.xml index 36e2a3b..028c242 100644 --- a/KinanCity-captcha-api/pom.xml +++ b/KinanCity-captcha-api/pom.xml @@ -27,7 +27,7 @@ junit junit - 4.12 + 4.13.1 test From dd48b42a00282c0b814611e3cb7e13baa858d136 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 03:39:24 +0000 Subject: [PATCH 13/17] build(deps-dev): bump junit in /KinanCity-captcha-client Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1) Signed-off-by: dependabot[bot] --- KinanCity-captcha-client/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KinanCity-captcha-client/pom.xml b/KinanCity-captcha-client/pom.xml index c326695..42fd9a3 100644 --- a/KinanCity-captcha-client/pom.xml +++ b/KinanCity-captcha-client/pom.xml @@ -26,7 +26,7 @@ junit junit - 4.12 + 4.13.1 test From c67d46b2c849fbf1e4a1e0ef9c1e7dd40585a94e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 03:43:16 +0000 Subject: [PATCH 14/17] build(deps-dev): bump junit in /KinanCity-captcha-server Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1) Signed-off-by: dependabot[bot] --- KinanCity-captcha-server/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KinanCity-captcha-server/pom.xml b/KinanCity-captcha-server/pom.xml index 3eab0b2..eeb4d34 100644 --- a/KinanCity-captcha-server/pom.xml +++ b/KinanCity-captcha-server/pom.xml @@ -78,7 +78,7 @@ junit junit - 4.12 + 4.13.1 test From 6a6ff8745e695a5b99682ee3b688cbbc389fe660 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 03:45:02 +0000 Subject: [PATCH 15/17] build(deps-dev): bump junit in /KinanCity-captcha-2captcha Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1) Signed-off-by: dependabot[bot] --- KinanCity-captcha-2captcha/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KinanCity-captcha-2captcha/pom.xml b/KinanCity-captcha-2captcha/pom.xml index ae260ef..61e4616 100644 --- a/KinanCity-captcha-2captcha/pom.xml +++ b/KinanCity-captcha-2captcha/pom.xml @@ -37,7 +37,7 @@ junit junit - 4.12 + 4.13.1 test From 3d6f1a1b85ebce1686217fa0b61eb159f4d39454 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 04:36:23 +0000 Subject: [PATCH 16/17] build(deps-dev): bump junit from 4.12 to 4.13.1 in /KinanCity-mail Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1) Signed-off-by: dependabot[bot] --- KinanCity-mail/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KinanCity-mail/pom.xml b/KinanCity-mail/pom.xml index ad202f6..c06132b 100644 --- a/KinanCity-mail/pom.xml +++ b/KinanCity-mail/pom.xml @@ -48,7 +48,7 @@ junit junit - 4.12 + 4.13.1 test From da8d9f5488a07a5462947c95af5ca8ed3d6bb0af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 03:51:10 +0000 Subject: [PATCH 17/17] build(deps-dev): bump junit in /KinanCity-captcha-AntiCaptcha Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1) Signed-off-by: dependabot[bot] --- KinanCity-captcha-AntiCaptcha/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KinanCity-captcha-AntiCaptcha/pom.xml b/KinanCity-captcha-AntiCaptcha/pom.xml index f7f2e01..79bfc84 100644 --- a/KinanCity-captcha-AntiCaptcha/pom.xml +++ b/KinanCity-captcha-AntiCaptcha/pom.xml @@ -33,7 +33,7 @@ junit junit - 4.12 + 4.13.1 test