view src/nabble/view/web/forum/ChangeDomainName.java @ 50:806d3297f92b

allow custom nabble domains
author Franklin Schmidt <fschmidt@gmail.com>
date Sun, 27 Jun 2021 18:26:58 -0600
parents 18cf4872fd7f
children
line wrap: on
line source


package nabble.view.web.forum;

import fschmidt.util.mail.Mail;
import fschmidt.util.mail.MailAddress;
import fschmidt.util.mail.MailException;
import fschmidt.util.mail.MailHome;
import fschmidt.util.mail.PlainTextContent;
import nabble.model.ModelException;
import nabble.model.ModelHome;
import nabble.model.Node;
import nabble.model.Site;
import nabble.model.User;
import nabble.naml.namespaces.TemplateException;
import nabble.view.lib.Jtp;
import nabble.view.lib.Permissions;
import nabble.view.lib.Shared;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.UnknownHostException;


public final class ChangeDomainName extends HttpServlet {

	protected void service(HttpServletRequest request,HttpServletResponse response)
		throws ServletException, IOException
	{
		Site site = ModelHome.getSite(Jtp.getLong(request, "site"));
		if (site == null) {
			response.sendError(HttpServletResponse.SC_NOT_FOUND, "No application match the \"<i>" + request.getHeader("host") + "</i>\" domain.");
			return;
		}

		User user = Jtp.getUser(request);
		if (user == null) {
			response.sendRedirect("http://" + Jtp.getDefaultBaseUrl(site) + Jtp.loginPath(site,null,response.encodeURL("/forum/ChangeDomainName.jtp?site="+site.getId())) );
			return;
		}

		boolean isSiteAdmin = Permissions.isInGroup(user, Permissions.ADMINISTRATORS_GROUP);
		boolean isSysAdmin = Permissions.isSysAdmin(user);
		if (!isSiteAdmin && !isSysAdmin) {
			Jtp.login("Only administrators can proceed in this area.", request, response);
			return;
		}

		Node rootNode = site.getRootNode();
		boolean isSave = "save".equals(request.getParameter("action"));

		String domainName = site.getCustomDomain();
		String domainOption = domainName == null? "default-domain" : "own-domain";
		if (isSave) {
			domainName = request.getParameter("domain-name");
			domainOption = request.getParameter("domain-option");
		}

		String errorMsg = null;
		if (isSave && "POST".equals(request.getMethod())) {
			try {
				boolean isDefault = "default-domain".equals(domainOption);
				if (isDefault)
					site.setCustomDomain(null);
				else {
					domainName = domainName.trim();
					if (domainName.length() == 0 || domainName.equals(InetAddress.getLocalHost().getHostAddress()))
						throw ModelException.newInstance("invalid_domain", "Please enter a valid domain name for your application.");
					Long currentSiteId = ModelHome.getSiteIdFromDomain(domainName);
					if (currentSiteId != null && !currentSiteId.equals(site.getId()))
						throw ModelException.newInstance("domain_already_in_use", "This domain is already in use.");
					String domainIP = getDomainIP(domainName);
					String nabbleIP = getDomainIP(Jtp.getDefaultHost());
					if (!nabbleIP.equals(domainIP))
						throw ModelException.newInstance(
							"domain_bad_resolution",
							"Your domain name currently resolves to an IP address that doesn't belong to Nabble. " +
							"Please follow the instructions (see link below) in order to update your DNS. " +
							"If you have just updated your DNS, please wait 24 hours for the changes to take affect.");

					site.setCustomDomain(domainName);
				}
				sendEmail(user, site);
				response.sendRedirect(Jtp.url(rootNode));
				return;
			} catch (TemplateException e) {
				errorMsg = e.getMessage();
			}
		}

		PrintWriter out = response.getWriter();
		
		out.print( "\n<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n<html>\n	<head>\n		<META NAME=\"robots\" CONTENT=\"noindex,nofollow\">\n		" );
 Shared.title(request, response, "Change Domain Name"); 
		out.print( "\n		<style type=\"text/css\">\n			label { font-weight:bold; }\n			.step {\n				font-weight: bold;\n				font-size: 120%;\n				padding: .5em .3em;\n				white-space:nowrap;\n			}\n		</style>\n		<script type=\"text/javascript\">\n			$(document).ready(function() {\n				function enableControls() {\n					if ($('#default-domain').attr('checked')) {\n						$('#domain-name').attr('disabled','y');\n					} else {\n						$('#domain-name').removeAttr('disabled').focus();\n					}\n				};\n				$('#default-domain,#own-domain').change(enableControls);\n				enableControls();\n			});\n		</script>\n	</head>\n	<body>\n		" );
 Shared.minHeader(request,response, rootNode); 
		out.print( "\n		" );
 Shared.editHeader(rootNode.getSubjectHtml(), "Change Domain Name", out); 
		out.print( "\n		" );
 Shared.errorMessage(request, response, errorMsg, null); 
		out.print( "\n\n		" );
 if (site.getCustomDomain() != null) { 
		out.print( "\n			<div class=\"info-message rounded\" style=\"padding: .5em;margin:.4em 0\">\n				<b>You already have a domain set for \"" );
		out.print( (rootNode.getSubject()) );
		out.print( "\".</b><br/>\n				If you change this domain configuration, links to the old domain will probably stop working.\n			</div>\n		" );
 } 
		out.print( "\n\n		<div class=\"field weak-color\" style=\"margin-left:1.5em\">\n			<div style=\"margin:1em 0 .5em\">\n				Access your application with a custom domain name.\n			</div>\n\n			<form method=\"post\" action=\"/forum/ChangeDomainName.jtp\" accept-charset=\"UTF-8\">\n				<input type=\"hidden\" name=\"action\" value=\"save\"/>\n				<input type=\"hidden\" name=\"site\" value=\"" );
		out.print( (site.getId()) );
		out.print( "\"/>\n				<input type=\"radio\" name=\"domain-option\" id=\"default-domain\" value=\"default-domain\" " );
		out.print( ("default-domain".equals(domainOption)?"checked='y'":"") );
		out.print( "/>\n				<label for=\"default-domain\">Nabble Default URL</label>\n				<div style=\"margin:.5em 0 1em 3em\">\n					http://" );
		out.print( (Jtp.getDefaultBaseUrl(site)) );
		out.print( "/\n				</div>\n\n				<input type=\"radio\" name=\"domain-option\" id=\"own-domain\" value=\"own-domain\" " );
		out.print( ("own-domain".equals(domainOption)?"checked='y'":"") );
		out.print( "/>\n				<label for=\"own-domain\">Your Own Domain</label>\n				<div style=\"margin:.5em 0 1em 3em\">\n					<table class=\"weak-color\">\n						<tr style=\"vertical-align:top\">\n							<td style=\"padding-right:.8em\"><div class=\"second-font shaded-bg-color rounded step border1 medium-border-color\">Step 1</div></td>\n							<td>\n								Go to your Domain Registrar and set the CNAME-record for your custom domain to <b>" );
		out.print( (Jtp.getDefaultHost()) );
		out.print( "</b>.\n								(<a href=\"/help/DNSConfiguration.jtp\" target=\"_new\">show me more details</a>)\n								<div style=\"margin-top:.7em;font-size:80%\">\n									(After this change, wait a little bit for the DNS to propagate.\n									Then open your custom domain url in the browser to see if the change is propagated &ndash; if successful, you should get redirected to the Nabble home page)\n								</div>\n							</td>\n						</tr>\n						<tr style=\"vertical-align:top\">\n							<td style=\"padding:1.7em .5em 0 0\"><div class=\"second-font shaded-bg-color rounded step border1 medium-border-color\">Step 2</div></td>\n							<td style=\"padding-top:1.5em\">\n								<div style=\"margin-bottom:.3em\">Enter your custom domain name below:</div>\n								http://<input id=\"domain-name\" name=\"domain-name\" size=\"40\" value=\"" );
		out.print( (Jtp.hideNull(domainName)) );
		out.print( "\"/>\n								<div style=\"font-size:80%;margin:.3em 0\">\n									Example: mydomain.com\n								</div>\n							</td>\n						</tr>\n						<tr style=\"vertical-align:top\">\n							<td style=\"padding:1.3em .5em 0 0\"><div class=\"second-font shaded-bg-color rounded step border1 medium-border-color\">Step 3</div></td>\n							<td style=\"padding-top:1.1em\">\n								After saving your changes, your old URLs will continue to work and will automatically redirect to your custom domain URL.\n								You will also receive an email with a link to revert this domain change.\n							</td>\n						</tr>\n					</table>\n				</div>\n\n				<input type=\"submit\" value=\"Save Changes\" />\n				or <a href=\"" );
		out.print( (Jtp.path(rootNode)) );
		out.print( "\">Cancel</a>\n			</form>\n		</div>\n		" );
 Shared.footer(request, response); 
		out.print( "\n		" );
 Shared.analytics(request, response); 
		out.print( "\n	</body>\n</html>\n" );

	}

	private static String getDomainIP(String domain) {
		String ip = domain;
		int i = ip.indexOf(":");
		if( i > 0 )
			ip = ip.substring(0,i);
		try {
			InetAddress add = InetAddress.getByName(ip);
			if (add != null)
				ip = add.getHostAddress();
		} catch(UnknownHostException e) {}
		return ip;
	}

	private static void sendEmail(User user, Site site)
		throws IOException, ServletException
	{
		StringBuilder builder = new StringBuilder();
		builder.append("Dear ").append(user.getName()).append(",\n\n");
		builder.append("You have changed your domain name configuration to:\n");
		builder.append(site.getBaseUrl());
		builder.append("\n\n");
		builder.append("If you have problems with your domain name or simply want to change this configuration again, you can use this link:\n");
		builder.append("http://").append(Jtp.getDefaultHost()).append("/forum/ChangeDomainName.jtp?site=").append(site.getId());
		builder.append("\n\n");
		builder.append("Regards,\n");
		builder.append("The Nabble team");

		try {
			Mail mail = MailHome.newMail();
			mail.setFrom(new MailAddress(ModelHome.noReply, "Nabble"));
			mail.setTo(new MailAddress(user.getEmail()));
			mail.setSubject("Domain name configuration changed");
			mail.setContent(new PlainTextContent(builder.toString()));
			ModelHome.send(mail);
		} catch(MailException e) {
			throw new RuntimeException(e);
		}
	}
}