aboutsummaryrefslogtreecommitdiff
path: root/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/service
diff options
context:
space:
mode:
Diffstat (limited to 'subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/service')
-rw-r--r--subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/service/EmailSession.java108
-rw-r--r--subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/service/LicenseGenerator.java170
-rw-r--r--subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/service/WhitelistGenerator.java53
3 files changed, 331 insertions, 0 deletions
diff --git a/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/service/EmailSession.java b/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/service/EmailSession.java
new file mode 100644
index 00000000..cfd96651
--- /dev/null
+++ b/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/service/EmailSession.java
@@ -0,0 +1,108 @@
+/*
+ This file is part of Subsonic.
+
+ Subsonic is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Subsonic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
+
+ Copyright 2010 (C) Sindre Mehus
+ */
+package net.sourceforge.subsonic.backend.service;
+
+import net.sourceforge.subsonic.backend.Util;
+
+import javax.mail.Folder;
+import javax.mail.Session;
+import javax.mail.Message;
+import javax.mail.Address;
+import javax.mail.MessagingException;
+import javax.mail.Store;
+import javax.mail.Transport;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.AddressException;
+import java.util.Properties;
+import java.util.List;
+
+/**
+ * @author Sindre Mehus
+ */
+public class EmailSession {
+
+ private static final String SMTP_MAIL_SERVER = "smtp.gmail.com";
+ private static final String POP_MAIL_SERVER = "pop.gmail.com";
+ private static final String IMAP_MAIL_SERVER = "imap.gmail.com";
+ private static final String USER = "subsonic@activeobjects.no";
+
+ private Session session;
+ private String password;
+
+ public EmailSession() throws Exception {
+ Properties props = new Properties();
+// props.setProperty("mail.debug", "true");
+ props.setProperty("mail.store.protocol", "pop3s");
+ props.setProperty("mail.smtps.host", SMTP_MAIL_SERVER);
+ props.setProperty("mail.smtps.auth", "true");
+ props.setProperty("mail.smtps.timeout", "10000");
+ props.setProperty("mail.smtps.connectiontimeout", "10000");
+ props.setProperty("mail.pop3s.timeout", "10000");
+ props.setProperty("mail.pop3s.connectiontimeout", "10000");
+
+ session = Session.getDefaultInstance(props, null);
+ password = Util.getPassword("gmailpwd.txt");
+ }
+
+ public void sendMessage(String from, List<String> to, List<String> cc, List<String> bcc, List<String> replyTo,
+ String subject, String text) throws MessagingException {
+ Message message = new MimeMessage(session);
+
+ message.setFrom(new InternetAddress(from));
+ message.setReplyTo(new Address[]{new InternetAddress(from)});
+ message.setRecipients(Message.RecipientType.TO, convertAddress(to));
+ message.setRecipients(Message.RecipientType.CC, convertAddress(cc));
+ message.setRecipients(Message.RecipientType.BCC, convertAddress(bcc));
+ message.setReplyTo(convertAddress(replyTo));
+ message.setSubject(subject);
+ message.setText(text);
+
+ // Send the message
+ Transport transport = null;
+ try {
+ transport = session.getTransport("smtps");
+ transport.connect(USER, password);
+ transport.sendMessage(message, message.getAllRecipients());
+ } finally {
+ if (transport != null) {
+ transport.close();
+ }
+ }
+ }
+
+ public Folder getFolder(String name) throws Exception {
+ Store store = session.getStore("imaps");
+ store.connect(IMAP_MAIL_SERVER, USER, password);
+ Folder folder = store.getFolder(name);
+ folder.open(Folder.READ_ONLY);
+ return folder;
+ }
+
+ private Address[] convertAddress(List<String> addresses) throws AddressException {
+ if (addresses == null) {
+ return null;
+ }
+ Address[] result = new Address[addresses.size()];
+ for (int i = 0; i < addresses.size(); i++) {
+ result[i] = new InternetAddress(addresses.get(i));
+ }
+ return result;
+ }
+}
diff --git a/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/service/LicenseGenerator.java b/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/service/LicenseGenerator.java
new file mode 100644
index 00000000..77376a8e
--- /dev/null
+++ b/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/service/LicenseGenerator.java
@@ -0,0 +1,170 @@
+package net.sourceforge.subsonic.backend.service;
+
+import net.sourceforge.subsonic.backend.dao.PaymentDao;
+import net.sourceforge.subsonic.backend.domain.Payment;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.log4j.Logger;
+
+import javax.mail.MessagingException;
+import java.security.MessageDigest;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Runs a task at regular intervals, checking for incoming donations and sending
+ * out license keys by email.
+ *
+ * @author Sindre Mehus
+ * @version $Id$
+ */
+public class LicenseGenerator {
+
+ private static final Logger LOG = Logger.getLogger(LicenseGenerator.class);
+ private static final long DELAY = 60; // One minute.
+
+ private PaymentDao paymentDao;
+ private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
+
+ public void init() {
+ Runnable task = new Runnable() {
+ public void run() {
+ try {
+ LOG.info("Starting license generator.");
+ processPayments();
+ LOG.info("Completed license generator.");
+ } catch (Throwable x) {
+ LOG.error("Failed to process license emails.", x);
+ }
+ }
+ };
+ executor.scheduleWithFixedDelay(task, DELAY, DELAY, TimeUnit.SECONDS);
+ LOG.info("Scheduled license generator to run every " + DELAY + " seconds.");
+ }
+
+ private void processPayments() throws Exception {
+ List<Payment> payments = paymentDao.getPaymentsByProcessingStatus(Payment.ProcessingStatus.NEW);
+ LOG.info(payments.size() + " new payment(s).");
+ if (payments.isEmpty()) {
+ return;
+ }
+
+ EmailSession emailSession = new EmailSession();
+ for (Payment payment : payments) {
+ processPayment(payment, emailSession);
+ }
+ }
+
+ private void processPayment(Payment payment, EmailSession emailSession) {
+ try {
+ LOG.info("Processing " + payment);
+ String email = payment.getPayerEmail();
+ if (email == null) {
+ throw new Exception("Missing email address.");
+ }
+
+ boolean eligible = isEligible(payment);
+ boolean ignorable = isIgnorable(payment);
+ if (eligible) {
+ sendLicenseTo(email, emailSession);
+ LOG.info("Sent license key for " + payment);
+ } else {
+ LOG.info("Payment not eligible for " + payment);
+ }
+
+ if (eligible || ignorable) {
+ payment.setProcessingStatus(Payment.ProcessingStatus.COMPLETED);
+ payment.setLastUpdated(new Date());
+ paymentDao.updatePayment(payment);
+ }
+
+ } catch (Throwable x) {
+ LOG.error("Failed to process " + payment, x);
+ }
+ }
+
+ private boolean isEligible(Payment payment) {
+ String status = payment.getPaymentStatus();
+ if ("echeck".equalsIgnoreCase(payment.getPaymentType())) {
+ return "Pending".equalsIgnoreCase(status) || "Completed".equalsIgnoreCase(status);
+ }
+ return "Completed".equalsIgnoreCase(status);
+ }
+
+ private boolean isIgnorable(Payment payment) {
+ String status = payment.getPaymentStatus();
+ return "Denied".equalsIgnoreCase(status) ||
+ "Reversed".equalsIgnoreCase(status) ||
+ "Refunded".equalsIgnoreCase(status);
+ }
+
+ public void sendLicenseTo(String to, EmailSession emailSession) throws MessagingException {
+ emailSession.sendMessage("subsonic_donation@activeobjects.no",
+ Arrays.asList(to),
+ null,
+ Arrays.asList("subsonic_donation@activeobjects.no", "sindre@activeobjects.no"),
+ Arrays.asList("subsonic_donation@activeobjects.no"),
+ "Subsonic License",
+ createLicenseContent(to));
+ LOG.info("Sent license to " + to);
+ }
+
+ private String createLicenseContent(String to) {
+ String license = md5Hex(to.toLowerCase());
+
+ return "Dear Subsonic donor,\n" +
+ "\n" +
+ "Many thanks for your kind donation to Subsonic!\n" +
+ "Please find your license key below.\n" +
+ "\n" +
+ "Email: " + to + "\n" +
+ "License: " + license + " \n" +
+ "\n" +
+ "To install the license key, click the \"Donate\" link in the top right corner of the Subsonic web interface.\n" +
+ "\n" +
+ "More info here: http://subsonic.org/pages/getting-started.jsp#3\n" +
+ "\n" +
+ "This license is valid for personal, non-commercial of Subsonic. For commercial use, please contact us for licensing options.\n" +
+ "\n" +
+ "Thanks again for supporting the project!\n" +
+ "\n" +
+ "Best regards,\n" +
+ "The Subsonic team";
+ }
+
+ /**
+ * Calculates the MD5 digest and returns the value as a 32 character hex string.
+ *
+ * @param s Data to digest.
+ * @return MD5 digest as a hex string.
+ */
+ private String md5Hex(String s) {
+ if (s == null) {
+ return null;
+ }
+
+ try {
+ MessageDigest md5 = MessageDigest.getInstance("MD5");
+ return new String(Hex.encodeHex(md5.digest(s.getBytes("UTF-8"))));
+ } catch (Exception x) {
+ throw new RuntimeException(x.getMessage(), x);
+ }
+ }
+
+ public void setPaymentDao(PaymentDao paymentDao) {
+ this.paymentDao = paymentDao;
+ }
+
+ public static void main(String[] args) throws Exception {
+ String address = args[0];
+// String license = md5Hex(address.toLowerCase());
+// System.out.println("Email: " + address);
+// System.out.println("License: " + license);
+
+ LicenseGenerator generator = new LicenseGenerator();
+ generator.sendLicenseTo(address, new EmailSession());
+ }
+}
diff --git a/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/service/WhitelistGenerator.java b/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/service/WhitelistGenerator.java
new file mode 100644
index 00000000..eef29611
--- /dev/null
+++ b/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/service/WhitelistGenerator.java
@@ -0,0 +1,53 @@
+package net.sourceforge.subsonic.backend.service;
+
+import net.sourceforge.subsonic.backend.dao.PaymentDao;
+import org.apache.log4j.Logger;
+
+import javax.mail.Address;
+import javax.mail.Folder;
+import javax.mail.Message;
+import javax.mail.internet.InternetAddress;
+import java.util.Date;
+
+/**
+ * Creates a license whitelist.
+ *
+ * @author Sindre Mehus
+ * @version $Id$
+ */
+public class WhitelistGenerator {
+
+ private static final Logger LOG = Logger.getLogger(WhitelistGenerator.class);
+
+ private PaymentDao paymentDao;
+
+ public void generate(Date newerThan) throws Exception {
+ LOG.info("Starting whitelist update for emails newer than " + newerThan);
+
+ EmailSession session = new EmailSession();
+ Folder folder = session.getFolder("[Gmail]/Sent Mail");
+ int n = folder.getMessageCount();
+
+ for (int i = n; i >= 0; i--) {
+ Message message = folder.getMessage(i);
+ Date date = message.getSentDate();
+ InternetAddress address = (InternetAddress) message.getRecipients(Message.RecipientType.TO)[0];
+ String recipient = address.getAddress();
+ if (date.before(newerThan)) {
+ break;
+ }
+ LOG.info(date + " " + recipient);
+
+ if (paymentDao.getPaymentByEmail(recipient) == null && !paymentDao.isWhitelisted(recipient)) {
+ paymentDao.whitelist(recipient);
+ LOG.info("WHITELISTED " + recipient);
+ }
+ }
+ folder.close(false);
+ LOG.info("Completed whitelist update.");
+ }
+
+ public void setPaymentDao(PaymentDao paymentDao) {
+ this.paymentDao = paymentDao;
+ }
+}