aboutsummaryrefslogtreecommitdiff
path: root/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller
diff options
context:
space:
mode:
authorScott Jackson <daneren2005@gmail.com>2012-07-02 21:24:02 -0700
committerScott Jackson <daneren2005@gmail.com>2012-07-02 21:24:02 -0700
commita1a18f77a50804e0127dfa4b0f5240c49c541184 (patch)
tree19a38880afe505beddb5590379a8134d7730a277 /subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller
parentb61d787706979e7e20f4c3c4f93c1f129d92273f (diff)
downloaddsub-a1a18f77a50804e0127dfa4b0f5240c49c541184.tar.gz
dsub-a1a18f77a50804e0127dfa4b0f5240c49c541184.tar.bz2
dsub-a1a18f77a50804e0127dfa4b0f5240c49c541184.zip
Initial Commit
Diffstat (limited to 'subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller')
-rw-r--r--subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller/IPNController.java152
-rw-r--r--subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller/MultiController.java256
-rw-r--r--subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller/RedirectionController.java155
-rw-r--r--subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller/RedirectionManagementController.java273
4 files changed, 836 insertions, 0 deletions
diff --git a/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller/IPNController.java b/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller/IPNController.java
new file mode 100644
index 00000000..f6ced54f
--- /dev/null
+++ b/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller/IPNController.java
@@ -0,0 +1,152 @@
+/*
+ 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 2009 (C) Sindre Mehus
+ */
+package net.sourceforge.subsonic.backend.controller;
+
+import org.apache.log4j.Logger;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.client.BasicResponseHandler;
+import org.apache.http.params.HttpConnectionParams;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.mvc.Controller;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.net.URLEncoder;
+import java.util.Enumeration;
+import java.util.Date;
+import java.io.UnsupportedEncodingException;
+
+import net.sourceforge.subsonic.backend.domain.Payment;
+import net.sourceforge.subsonic.backend.dao.PaymentDao;
+
+/**
+ * Processes IPNs (Instant Payment Notifications) from PayPal.
+ *
+ * @author Sindre Mehus
+ */
+public class IPNController implements Controller {
+
+ private static final Logger LOG = Logger.getLogger(IPNController.class);
+
+ private static final String PAYPAL_URL = "https://www.paypal.com/cgi-bin/webscr";
+
+ private PaymentDao paymentDao;
+
+ public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ try {
+
+ LOG.info("Incoming IPN from " + request.getRemoteAddr());
+
+ String url = createValidationURL(request);
+ if (validate(url)) {
+ LOG.info("Verified payment. " + url);
+ } else {
+ LOG.warn("Failed to verify payment. " + url);
+ }
+ createOrUpdatePayment(request);
+
+ return null;
+ } catch (Exception x) {
+ LOG.error("Failed to process IPN.", x);
+ throw x;
+ }
+ }
+
+ private String createValidationURL(HttpServletRequest request) throws UnsupportedEncodingException {
+ Enumeration<?> en = request.getParameterNames();
+ StringBuilder url = new StringBuilder(PAYPAL_URL).append("?cmd=_notify-validate");
+ String encoding = request.getParameter("charset");
+ if (encoding == null) {
+ encoding = "ISO-8859-1";
+ }
+
+ while (en.hasMoreElements()) {
+ String paramName = (String) en.nextElement();
+ String paramValue = request.getParameter(paramName);
+ url.append("&").append(paramName).append("=").append(URLEncoder.encode(paramValue, encoding));
+ }
+
+ return url.toString();
+ }
+
+ private void createOrUpdatePayment(HttpServletRequest request) {
+ String item = request.getParameter("item_number");
+ if (item == null) {
+ item = request.getParameter("item_number1");
+ }
+ String paymentStatus = request.getParameter("payment_status");
+ String paymentType = request.getParameter("payment_type");
+ int paymentAmount = Math.round(new Float(request.getParameter("mc_gross")));
+ String paymentCurrency = request.getParameter("mc_currency");
+ String txnId = request.getParameter("txn_id");
+ String txnType = request.getParameter("txn_type");
+ String payerEmail = request.getParameter("payer_email");
+ String payerFirstName = request.getParameter("first_name");
+ String payerLastName = request.getParameter("last_name");
+ String payerCountry = request.getParameter("address_country");
+
+ Payment payment = paymentDao.getPaymentByTransactionId(txnId);
+ if (payment == null) {
+ payment = new Payment(null, txnId, txnType, item, paymentType, paymentStatus,
+ paymentAmount, paymentCurrency, payerEmail, payerFirstName, payerLastName,
+ payerCountry, Payment.ProcessingStatus.NEW, new Date(), new Date());
+ paymentDao.createPayment(payment);
+ } else {
+ payment.setTransactionType(txnType);
+ payment.setItem(item);
+ payment.setPaymentType(paymentType);
+ payment.setPaymentStatus(paymentStatus);
+ payment.setPaymentAmount(paymentAmount);
+ payment.setPaymentCurrency(paymentCurrency);
+ payment.setPayerEmail(payerEmail);
+ payment.setPayerFirstName(payerFirstName);
+ payment.setPayerLastName(payerLastName);
+ payment.setPayerCountry(payerCountry);
+ payment.setLastUpdated(new Date());
+ paymentDao.updatePayment(payment);
+ }
+
+ LOG.info("Received " + payment);
+ }
+
+ private boolean validate(String url) throws Exception {
+ HttpClient client = new DefaultHttpClient();
+ HttpConnectionParams.setConnectionTimeout(client.getParams(), 60000);
+ HttpConnectionParams.setSoTimeout(client.getParams(), 60000);
+ HttpGet method = new HttpGet(url);
+ String content;
+ try {
+ ResponseHandler<String> responseHandler = new BasicResponseHandler();
+ content = client.execute(method, responseHandler);
+
+ LOG.info("Validation result: " + content);
+ return "VERIFIED".equals(content);
+ } finally {
+ client.getConnectionManager().shutdown();
+ }
+ }
+
+ public void setPaymentDao(PaymentDao paymentDao) {
+ this.paymentDao = paymentDao;
+ }
+
+}
diff --git a/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller/MultiController.java b/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller/MultiController.java
new file mode 100644
index 00000000..cdc1674a
--- /dev/null
+++ b/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller/MultiController.java
@@ -0,0 +1,256 @@
+/*
+ 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 2009 (C) Sindre Mehus
+ */
+package net.sourceforge.subsonic.backend.controller;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Arrays;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import net.sourceforge.subsonic.backend.dao.PaymentDao;
+import net.sourceforge.subsonic.backend.service.LicenseGenerator;
+import net.sourceforge.subsonic.backend.service.WhitelistGenerator;
+import org.apache.log4j.Logger;
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.springframework.web.bind.ServletRequestBindingException;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
+import org.springframework.web.bind.ServletRequestUtils;
+import org.springframework.jdbc.core.ColumnMapRowMapper;
+import org.springframework.dao.DataAccessException;
+import net.sourceforge.subsonic.backend.dao.DaoHelper;
+import net.sourceforge.subsonic.backend.Util;
+import net.sourceforge.subsonic.backend.service.EmailSession;
+
+/**
+ * Multi-controller used for simple pages.
+ *
+ * @author Sindre Mehus
+ */
+public class MultiController extends MultiActionController {
+
+ private static final Logger LOG = Logger.getLogger(RedirectionController.class);
+
+ private static final String SUBSONIC_VERSION = "4.6";
+ private static final String SUBSONIC_BETA_VERSION = "4.7.beta2";
+
+ private static final Date LICENSE_DATE_THRESHOLD;
+
+ private DaoHelper daoHelper;
+
+ private PaymentDao paymentDao;
+ private WhitelistGenerator whitelistGenerator;
+ private LicenseGenerator licenseGenerator;
+
+ static {
+ Calendar calendar = Calendar.getInstance();
+ calendar.clear();
+ calendar.set(Calendar.YEAR, 2010);
+ calendar.set(Calendar.MONTH, Calendar.JUNE);
+ calendar.set(Calendar.DAY_OF_MONTH, 19);
+ LICENSE_DATE_THRESHOLD = calendar.getTime();
+ }
+
+ public ModelAndView version(HttpServletRequest request, HttpServletResponse response) throws Exception {
+
+ String localVersion = request.getParameter("v");
+ LOG.info(request.getRemoteAddr() + " asked for latest version. Local version: " + localVersion);
+
+ PrintWriter writer = response.getWriter();
+
+ writer.println("SUBSONIC_VERSION_BEGIN" + SUBSONIC_VERSION + "SUBSONIC_VERSION_END");
+ writer.println("SUBSONIC_FULL_VERSION_BEGIN" + SUBSONIC_VERSION + "SUBSONIC_FULL_VERSION_END");
+ writer.println("SUBSONIC_BETA_VERSION_BEGIN" + SUBSONIC_BETA_VERSION + "SUBSONIC_BETA_VERSION_END");
+
+ return null;
+ }
+
+ public ModelAndView validateLicense(HttpServletRequest request, HttpServletResponse response) throws Exception {
+
+ String email = request.getParameter("email");
+ Long date = ServletRequestUtils.getLongParameter(request, "date");
+
+ boolean valid = isLicenseValid(email, date);
+ LOG.info(request.getRemoteAddr() + " asked to validate license for " + email + ". Result: " + valid);
+
+ PrintWriter writer = response.getWriter();
+ writer.println(valid);
+
+ return null;
+ }
+
+ public ModelAndView sendMail(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ String from = request.getParameter("from");
+ String to = request.getParameter("to");
+ String subject = request.getParameter("subject");
+ String text = request.getParameter("text");
+
+ EmailSession session = new EmailSession();
+ session.sendMessage(from, Arrays.asList(to), null, null, null, subject, text);
+
+ LOG.info("Sent email on behalf of " + request.getRemoteAddr() + " to " + to + " with subject '" + subject + "'");
+
+ return null;
+ }
+
+ public ModelAndView db(HttpServletRequest request, HttpServletResponse response) throws Exception {
+
+ if (!authenticate(request, response)) {
+ return null;
+ }
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ map.put("p", request.getParameter("p"));
+ String query = request.getParameter("query");
+ if (query != null) {
+ map.put("query", query);
+
+ try {
+ List<?> result = daoHelper.getJdbcTemplate().query(query, new ColumnMapRowMapper());
+ map.put("result", result);
+ } catch (DataAccessException x) {
+ map.put("error", ExceptionUtils.getRootCause(x).getMessage());
+ }
+ }
+
+ return new ModelAndView("backend/db", "model", map);
+ }
+
+ public ModelAndView payment(HttpServletRequest request, HttpServletResponse response) throws Exception {
+
+ if (!authenticate(request, response)) {
+ return null;
+ }
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ Calendar startOfToday = Calendar.getInstance();
+ startOfToday.set(Calendar.HOUR_OF_DAY, 0);
+ startOfToday.set(Calendar.MINUTE, 0);
+ startOfToday.set(Calendar.SECOND, 0);
+ startOfToday.set(Calendar.MILLISECOND, 0);
+
+ Calendar endOfToday = Calendar.getInstance();
+ endOfToday.setTime(startOfToday.getTime());
+ endOfToday.add(Calendar.DATE, 1);
+
+ Calendar startOfYesterday = Calendar.getInstance();
+ startOfYesterday.setTime(startOfToday.getTime());
+ startOfYesterday.add(Calendar.DATE, -1);
+
+ Calendar startOfMonth = Calendar.getInstance();
+ startOfMonth.setTime(startOfToday.getTime());
+ startOfMonth.set(Calendar.DATE, 1);
+
+ int sumToday = paymentDao.getPaymentAmount(startOfToday.getTime(), endOfToday.getTime());
+ int sumYesterday = paymentDao.getPaymentAmount(startOfYesterday.getTime(), startOfToday.getTime());
+ int sumMonth = paymentDao.getPaymentAmount(startOfMonth.getTime(), endOfToday.getTime());
+ int dayAverageThisMonth = sumMonth / startOfToday.get(Calendar.DATE);
+
+ map.put("sumToday", sumToday);
+ map.put("sumYesterday", sumYesterday);
+ map.put("sumMonth", sumMonth);
+ map.put("dayAverageThisMonth", dayAverageThisMonth);
+
+ return new ModelAndView("backend/payment", "model", map);
+ }
+
+ public ModelAndView requestLicense(HttpServletRequest request, HttpServletResponse response) throws Exception {
+
+ String email = request.getParameter("email");
+ boolean valid = email != null && isLicenseValid(email, System.currentTimeMillis());
+ if (valid) {
+ EmailSession session = new EmailSession();
+ licenseGenerator.sendLicenseTo(email, session);
+ }
+
+ Map<String, Object> map = new HashMap<String, Object>();
+
+ map.put("email", email);
+ map.put("valid", valid);
+
+ return new ModelAndView("backend/requestLicense", "model", map);
+ }
+
+ public ModelAndView whitelist(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ if (!authenticate(request, response)) {
+ return null;
+ }
+
+ Date newerThan = MultiController.LICENSE_DATE_THRESHOLD;
+ Integer days = ServletRequestUtils.getIntParameter(request, "days");
+ if (days != null) {
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.DATE, -days);
+ newerThan = cal.getTime();
+ }
+ whitelistGenerator.generate(newerThan);
+ return null;
+ }
+
+ private boolean authenticate(HttpServletRequest request, HttpServletResponse response) throws ServletRequestBindingException, IOException {
+ String password = ServletRequestUtils.getRequiredStringParameter(request, "p");
+ if (!password.equals(Util.getPassword("backendpwd.txt"))) {
+ response.sendError(HttpServletResponse.SC_FORBIDDEN);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean isLicenseValid(String email, Long date) {
+ if (email == null || date == null) {
+ return false;
+ }
+
+ if (paymentDao.isBlacklisted(email)) {
+ return false;
+ }
+
+ // Always accept licenses that are older than 2010-06-19.
+ if (date < LICENSE_DATE_THRESHOLD.getTime()) {
+ return true;
+ }
+
+ return paymentDao.getPaymentByEmail(email) != null || paymentDao.isWhitelisted(email);
+ }
+
+ public void setDaoHelper(DaoHelper daoHelper) {
+ this.daoHelper = daoHelper;
+ }
+
+ public void setPaymentDao(PaymentDao paymentDao) {
+ this.paymentDao = paymentDao;
+ }
+
+ public void setWhitelistGenerator(WhitelistGenerator whitelistGenerator) {
+ this.whitelistGenerator = whitelistGenerator;
+ }
+
+ public void setLicenseGenerator(LicenseGenerator licenseGenerator) {
+ this.licenseGenerator = licenseGenerator;
+ }
+}
diff --git a/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller/RedirectionController.java b/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller/RedirectionController.java
new file mode 100644
index 00000000..3f36ec6d
--- /dev/null
+++ b/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller/RedirectionController.java
@@ -0,0 +1,155 @@
+/*
+ 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 2009 (C) Sindre Mehus
+ */
+package net.sourceforge.subsonic.backend.controller;
+
+import net.sourceforge.subsonic.backend.dao.RedirectionDao;
+import net.sourceforge.subsonic.backend.domain.Redirection;
+import static net.sourceforge.subsonic.backend.controller.RedirectionManagementController.RESERVED_REDIRECTS;
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.mvc.Controller;
+import org.springframework.web.servlet.view.RedirectView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.Date;
+import java.util.Enumeration;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Redirects vanity URLs (such as http://sindre.subsonic.org).
+ *
+ * @author Sindre Mehus
+ */
+public class RedirectionController implements Controller {
+
+ private static final Logger LOG = Logger.getLogger(RedirectionController.class);
+ private RedirectionDao redirectionDao;
+
+ public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
+
+ String redirectFrom = getRedirectFrom(request);
+ if (RESERVED_REDIRECTS.containsKey(redirectFrom)) {
+ LOG.info("Reserved redirection: " + redirectFrom);
+ return new ModelAndView(new RedirectView(RESERVED_REDIRECTS.get(redirectFrom)));
+ }
+
+ Redirection redirection = redirectFrom == null ? null : redirectionDao.getRedirection(redirectFrom);
+
+ if (redirection == null) {
+ LOG.info("No redirection found: " + redirectFrom);
+ return new ModelAndView(new RedirectView("http://subsonic.org/pages"));
+ }
+
+ redirection.setLastRead(new Date());
+ redirection.setReadCount(redirection.getReadCount() + 1);
+ redirectionDao.updateRedirection(redirection);
+
+ // Check for trial expiration (unless called from REST client for which the Subsonic server manages trial expiry).
+ if (isTrialExpired(redirection) && !isREST(request)) {
+ LOG.info("Expired redirection: " + redirectFrom);
+ return new ModelAndView(new RedirectView("http://subsonic.org/pages/redirect-expired.jsp?redirectFrom=" +
+ redirectFrom + "&expired=" + redirection.getTrialExpires().getTime()));
+ }
+
+ String requestUrl = getFullRequestURL(request);
+ String to = StringUtils.removeEnd(getRedirectTo(request, redirection), "/");
+ String redirectTo = requestUrl.replaceFirst("http://" + redirectFrom + "\\.subsonic\\.org", to);
+ LOG.info("Redirecting from " + requestUrl + " to " + redirectTo);
+
+ return new ModelAndView(new RedirectView(redirectTo));
+ }
+
+ private String getRedirectTo(HttpServletRequest request, Redirection redirection) {
+
+ // If the request comes from within the same LAN as the destination Subsonic
+ // server, redirect using the local IP address of the server.
+
+ String localRedirectTo = redirection.getLocalRedirectTo();
+ if (localRedirectTo != null) {
+ try {
+ URL url = new URL(redirection.getRedirectTo());
+ if (url.getHost().equals(request.getRemoteAddr())) {
+ return localRedirectTo;
+ }
+ } catch (Throwable x) {
+ LOG.error("Malformed local redirect URL.", x);
+ }
+ }
+
+ return redirection.getRedirectTo();
+ }
+
+ private boolean isTrialExpired(Redirection redirection) {
+ return redirection.isTrial() && redirection.getTrialExpires() != null && redirection.getTrialExpires().before(new Date());
+ }
+
+ private boolean isREST(HttpServletRequest request) {
+ return request.getParameter("c") != null;
+ }
+
+ private String getFullRequestURL(HttpServletRequest request) throws UnsupportedEncodingException {
+ StringBuilder builder = new StringBuilder(request.getRequestURL());
+
+ // For backwards compatibility; return query parameters in exact same sequence.
+ if ("GET".equalsIgnoreCase(request.getMethod())) {
+ if (request.getQueryString() != null) {
+ builder.append("?").append(request.getQueryString());
+ }
+ return builder.toString();
+ }
+
+ builder.append("?");
+
+ Enumeration<?> paramNames = request.getParameterNames();
+ while (paramNames.hasMoreElements()) {
+ String paramName = (String) paramNames.nextElement();
+ String[] paramValues = request.getParameterValues(paramName);
+ for (String paramValue : paramValues) {
+ String p = URLEncoder.encode(paramValue, "UTF-8");
+ builder.append(paramName).append("=").append(p).append("&");
+ }
+ }
+
+ return builder.toString();
+ }
+
+ private String getRedirectFrom(HttpServletRequest request) throws MalformedURLException {
+ URL url = new URL(request.getRequestURL().toString());
+ String host = url.getHost();
+
+ String redirectFrom;
+ if (host.contains(".")) {
+ redirectFrom = StringUtils.substringBefore(host, ".");
+ } else {
+ // For testing.
+ redirectFrom = request.getParameter("redirectFrom");
+ }
+
+ return StringUtils.lowerCase(redirectFrom);
+ }
+
+ public void setRedirectionDao(RedirectionDao redirectionDao) {
+ this.redirectionDao = redirectionDao;
+ }
+}
diff --git a/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller/RedirectionManagementController.java b/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller/RedirectionManagementController.java
new file mode 100644
index 00000000..01ff90fa
--- /dev/null
+++ b/subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller/RedirectionManagementController.java
@@ -0,0 +1,273 @@
+/*
+ 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 2009 (C) Sindre Mehus
+ */
+package net.sourceforge.subsonic.backend.controller;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.net.ssl.SSLPeerUnverifiedException;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.params.HttpConnectionParams;
+import org.springframework.web.bind.ServletRequestUtils;
+import org.springframework.web.servlet.mvc.multiaction.MultiActionController;
+
+import net.sourceforge.subsonic.backend.dao.RedirectionDao;
+import net.sourceforge.subsonic.backend.domain.Redirection;
+
+/**
+ * @author Sindre Mehus
+ */
+public class RedirectionManagementController extends MultiActionController {
+
+ private static final Logger LOG = Logger.getLogger(RedirectionManagementController.class);
+
+ public static final Map<String,String> RESERVED_REDIRECTS = new HashMap<String, String>();
+
+ static {
+ RESERVED_REDIRECTS.put("forum", "http://www.activeobjects.no/subsonic/forum/index.php");
+ RESERVED_REDIRECTS.put("www", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("web", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("ftp", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("mail", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("s", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("subsonic", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("gosubsonic", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("android", "http://www.subsonic.org/pages/android.jsp");
+ RESERVED_REDIRECTS.put("iphone", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("subair", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("m", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("link", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("share", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("mobile", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("mobil", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("phone", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("wap", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("db", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("shop", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("wiki", "http://www.subsonic.org/pages/index.jsp");
+ RESERVED_REDIRECTS.put("test", "http://www.subsonic.org/pages/index.jsp");
+ }
+
+ private RedirectionDao redirectionDao;
+
+ public void register(HttpServletRequest request, HttpServletResponse response) throws Exception {
+
+ String redirectFrom = StringUtils.lowerCase(ServletRequestUtils.getRequiredStringParameter(request, "redirectFrom"));
+ String licenseHolder = ServletRequestUtils.getStringParameter(request, "licenseHolder");
+ String serverId = ServletRequestUtils.getRequiredStringParameter(request, "serverId");
+ int port = ServletRequestUtils.getRequiredIntParameter(request, "port");
+ Integer localPort = ServletRequestUtils.getIntParameter(request, "localPort");
+ String localIp = ServletRequestUtils.getStringParameter(request, "localIp");
+ String contextPath = ServletRequestUtils.getRequiredStringParameter(request, "contextPath");
+ boolean trial = ServletRequestUtils.getBooleanParameter(request, "trial", false);
+
+ Date now = new Date();
+ Date trialExpires = null;
+ if (trial) {
+ trialExpires = new Date(ServletRequestUtils.getRequiredLongParameter(request, "trialExpires"));
+ }
+
+ if (RESERVED_REDIRECTS.containsKey(redirectFrom)) {
+ sendError(response, "\"" + redirectFrom + "\" is a reserved address. Please select another.");
+ return;
+ }
+
+ if (!redirectFrom.matches("(\\w|\\-)+")) {
+ sendError(response, "Illegal characters present in \"" + redirectFrom + "\". Please select another.");
+ return;
+ }
+
+ String host = request.getRemoteAddr();
+ URL url = new URL("http", host, port, "/" + contextPath);
+ String redirectTo = url.toExternalForm();
+
+ String localRedirectTo = null;
+ if (localIp != null && localPort != null) {
+ URL localUrl = new URL("http", localIp, localPort, "/" + contextPath);
+ localRedirectTo = localUrl.toExternalForm();
+ }
+
+ Redirection redirection = redirectionDao.getRedirection(redirectFrom);
+ if (redirection == null) {
+
+ // Delete other redirects for same server ID.
+ redirectionDao.deleteRedirectionsByServerId(serverId);
+
+ redirection = new Redirection(0, licenseHolder, serverId, redirectFrom, redirectTo, localRedirectTo, trial, trialExpires, now, null, 0);
+ redirectionDao.createRedirection(redirection);
+ LOG.info("Created " + redirection);
+
+ } else {
+
+ boolean sameServerId = serverId.equals(redirection.getServerId());
+ boolean sameLicenseHolder = licenseHolder != null && licenseHolder.equals(redirection.getLicenseHolder());
+
+ // Note: A licensed user can take over any expired trial domain.
+ boolean existingTrialExpired = redirection.getTrialExpires() != null && redirection.getTrialExpires().before(now);
+
+ if (sameServerId || sameLicenseHolder || (existingTrialExpired && !trial)) {
+ redirection.setLicenseHolder(licenseHolder);
+ redirection.setServerId(serverId);
+ redirection.setRedirectFrom(redirectFrom);
+ redirection.setRedirectTo(redirectTo);
+ redirection.setLocalRedirectTo(localRedirectTo);
+ redirection.setTrial(trial);
+ redirection.setTrialExpires(trialExpires);
+ redirection.setLastUpdated(now);
+ redirectionDao.updateRedirection(redirection);
+ LOG.info("Updated " + redirection);
+ } else {
+ sendError(response, "The web address \"" + redirectFrom + "\" is already in use. Please select another.");
+ }
+ }
+ }
+
+ public void unregister(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ String serverId = ServletRequestUtils.getStringParameter(request, "serverId");
+ if (!StringUtils.isEmpty(serverId)) {
+ redirectionDao.deleteRedirectionsByServerId(serverId);
+ }
+ }
+
+ public void get(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ String redirectFrom = StringUtils.lowerCase(ServletRequestUtils.getRequiredStringParameter(request, "redirectFrom"));
+
+ Redirection redirection = redirectionDao.getRedirection(redirectFrom);
+ if (redirection == null) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Web address " + redirectFrom + ".subsonic.org not registered.");
+ return;
+ }
+
+ PrintWriter writer = response.getWriter();
+ String url = redirection.getRedirectTo();
+ if (!url.endsWith("/")) {
+ url += "/";
+ }
+ writer.println(url);
+
+ url = redirection.getLocalRedirectTo();
+ if (!url.endsWith("/")) {
+ url += "/";
+ }
+ writer.println(url);
+ }
+
+ public void test(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ String redirectFrom = StringUtils.lowerCase(ServletRequestUtils.getRequiredStringParameter(request, "redirectFrom"));
+ PrintWriter writer = response.getWriter();
+
+ Redirection redirection = redirectionDao.getRedirection(redirectFrom);
+ String webAddress = redirectFrom + ".subsonic.org";
+ if (redirection == null) {
+ writer.print("Web address " + webAddress + " not registered.");
+ return;
+ }
+
+ if (redirection.getTrialExpires() != null && redirection.getTrialExpires().before(new Date())) {
+ writer.print("Trial period expired. Please donate to activate web address.");
+ return;
+ }
+
+ String url = redirection.getRedirectTo();
+ if (!url.endsWith("/")) {
+ url += "/";
+ }
+ url += "icons/favicon.ico";
+
+ HttpClient client = new DefaultHttpClient();
+ HttpConnectionParams.setConnectionTimeout(client.getParams(), 15000);
+ HttpConnectionParams.setSoTimeout(client.getParams(), 15000);
+ HttpGet method = new HttpGet(url);
+
+ try {
+ HttpResponse resp = client.execute(method);
+ StatusLine status = resp.getStatusLine();
+
+ if (status.getStatusCode() == HttpStatus.SC_OK) {
+ String msg = webAddress + " responded successfully.";
+ writer.print(msg);
+ LOG.info(msg);
+ } else {
+ String msg = webAddress + " returned HTTP error code " + status.getStatusCode() + " " + status.getReasonPhrase();
+ writer.print(msg);
+ LOG.info(msg);
+ }
+ } catch (SSLPeerUnverifiedException x) {
+ String msg = webAddress + " responded successfully, but could not authenticate it.";
+ writer.print(msg);
+ LOG.info(msg);
+
+ } catch (Throwable x) {
+ String msg = webAddress + " is registered, but could not connect to it. (" + x.getClass().getSimpleName() + ")";
+ writer.print(msg);
+ LOG.info(msg);
+ } finally {
+ client.getConnectionManager().shutdown();
+ }
+ }
+
+ private void sendError(HttpServletResponse response, String message) throws IOException {
+ response.getWriter().print(message);
+ response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+ }
+
+ public void dump(HttpServletRequest request, HttpServletResponse response) throws Exception {
+
+ File file = File.createTempFile("redirections", ".txt");
+ PrintWriter writer = new PrintWriter(file, "UTF-8");
+ try {
+ int offset = 0;
+ int count = 100;
+ while (true) {
+ List<Redirection> redirections = redirectionDao.getAllRedirections(offset, count);
+ if (redirections.isEmpty()) {
+ break;
+ }
+ offset += redirections.size();
+ for (Redirection redirection : redirections) {
+ writer.println(redirection);
+ }
+ }
+ LOG.info("Dumped redirections to " + file.getAbsolutePath());
+ } finally {
+ IOUtils.closeQuietly(writer);
+ }
+ }
+
+ public void setRedirectionDao(RedirectionDao redirectionDao) {
+ this.redirectionDao = redirectionDao;
+ }
+} \ No newline at end of file