diff options
Diffstat (limited to 'subsonic-backend/src/main/java/net/sourceforge/subsonic/backend/controller')
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 |