aboutsummaryrefslogtreecommitdiff
path: root/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/service/LicenseGenerator.java
blob: 77376a8ec197c209a6cc3c48db2b2701fff522c4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
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());       
    }
}