/*******************************************************************************
* Copyright (c) 2015 IBH SYSTEMS GmbH.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBH SYSTEMS GmbH - initial API and implementation
*******************************************************************************/
package org.eclipse.packagedrone.mail.web;
import java.io.IOException;
import java.lang.reflect.Method;
import java.security.Principal;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.servlet.annotation.HttpConstraint;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import org.eclipse.packagedrone.mail.java.DefaultMailService;
import org.eclipse.packagedrone.sec.DatabaseDetails;
import org.eclipse.packagedrone.sec.UserInformation;
import org.eclipse.packagedrone.sec.UserInformationPrincipal;
import org.eclipse.packagedrone.sec.web.controller.HttpConstraints;
import org.eclipse.packagedrone.sec.web.controller.HttpContraintControllerInterceptor;
import org.eclipse.packagedrone.sec.web.controller.Secured;
import org.eclipse.packagedrone.sec.web.controller.SecuredControllerInterceptor;
import org.eclipse.packagedrone.web.Controller;
import org.eclipse.packagedrone.web.LinkTarget;
import org.eclipse.packagedrone.web.ModelAndView;
import org.eclipse.packagedrone.web.RequestMapping;
import org.eclipse.packagedrone.web.RequestMethod;
import org.eclipse.packagedrone.web.ViewResolver;
import org.eclipse.packagedrone.web.common.CommonController;
import org.eclipse.packagedrone.web.common.InterfaceExtender;
import org.eclipse.packagedrone.web.common.menu.MenuEntry;
import org.eclipse.packagedrone.web.controller.ControllerInterceptor;
import org.eclipse.packagedrone.web.controller.binding.BindingResult;
import org.eclipse.packagedrone.web.controller.binding.RequestParameter;
import org.eclipse.packagedrone.web.controller.form.FormData;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Controller
@RequestMapping ( "/default.mail/config" )
@ViewResolver ( "/WEB-INF/views/%s.jsp" )
@Secured
@ControllerInterceptor ( SecuredControllerInterceptor.class )
@HttpConstraint ( rolesAllowed = "ADMIN" )
@ControllerInterceptor ( HttpContraintControllerInterceptor.class )
public class ConfigController implements InterfaceExtender
{
private final static Logger logger = LoggerFactory.getLogger ( ConfigController.class );
private ConfigurationAdmin admin;
private volatile DefaultMailService mailService;
private static final Method METHOD_INDEX = LinkTarget.getControllerMethod ( ConfigController.class, "index" );
public void setMailService ( final DefaultMailService mailService )
{
this.mailService = mailService;
}
public void unsetMailService ( final DefaultMailService mailService )
{
this.mailService = null;
}
public void setAdmin ( final ConfigurationAdmin admin )
{
this.admin = admin;
}
@RequestMapping
public ModelAndView index ()
{
final Map<String, Object> model = new HashMap<> ();
model.put ( "command", getCurrent () );
fillModel ( model );
return new ModelAndView ( "index", model );
}
private void fillModel ( final Map<String, Object> model )
{
model.put ( "servicePresent", this.mailService != null );
}
@RequestMapping ( method = RequestMethod.POST )
public ModelAndView update ( @Valid @FormData ( "command" ) final MailSettings settings, final BindingResult bindingResult)
{
final Map<String, Object> model = new HashMap<> ();
if ( !bindingResult.hasErrors () )
{
setCurrent ( settings );
}
// poor man's wait
int i = 10;
while ( this.mailService == null && i > 0 )
{
try
{
Thread.sleep ( 100 );
}
catch ( final InterruptedException e )
{
break;
}
i--;
}
fillModel ( model );
return new ModelAndView ( "index", model );
}
protected void setCurrent ( final MailSettings settings )
{
try
{
final Configuration cfg = this.admin.getConfiguration ( DefaultMailService.SERVICE_PID );
final Dictionary<String, Object> properties = new Hashtable<> ();
put ( properties, "username", settings.getUsername () );
put ( properties, "password", settings.getPassword () );
put ( properties, "from", settings.getFrom () );
put ( properties, "prefix", settings.getPrefix () );
put ( properties, DefaultMailService.PROPERTY_PREFIX + "mail.transport.protocol", "smtp" );
put ( properties, DefaultMailService.PROPERTY_PREFIX + "mail.smtp.host", settings.getHost () );
put ( properties, DefaultMailService.PROPERTY_PREFIX + "mail.smtp.port", settings.getPort () );
if ( settings.isEnableStartTls () )
{
put ( properties, DefaultMailService.PROPERTY_PREFIX + "mail.smtp.starttls.enable", "true" );
}
cfg.update ( properties );
}
catch ( final IOException e )
{
throw new RuntimeException ( "Failed to update mail server configuration", e );
}
}
private void put ( final Dictionary<String, Object> properties, final String key, final Object value )
{
if ( value instanceof String && ( (String)value ).isEmpty () )
{
return;
}
if ( value != null )
{
properties.put ( key, value );
}
}
protected MailSettings getCurrent ()
{
try
{
final Configuration cfg = this.admin.getConfiguration ( DefaultMailService.SERVICE_PID );
if ( cfg == null || cfg.getProperties () == null )
{
return createDefault ();
}
final MailSettings result = new MailSettings ();
result.setUsername ( getString ( cfg, "username" ) );
result.setPassword ( getString ( cfg, "password" ) );
result.setFrom ( getString ( cfg, "from" ) );
result.setPrefix ( getString ( cfg, "prefix" ) );
result.setHost ( getString ( cfg, DefaultMailService.PROPERTY_PREFIX + "mail.smtp.host" ) );
result.setPort ( getInteger ( cfg, DefaultMailService.PROPERTY_PREFIX + "mail.smtp.port" ) );
return result;
}
catch ( final IOException e )
{
return createDefault ();
}
}
private Integer getInteger ( final Configuration cfg, final String key )
{
final Object val = cfg.getProperties ().get ( key );
if ( val instanceof Number )
{
return ( (Number)val ).intValue ();
}
if ( val instanceof String )
{
try
{
return Integer.parseInt ( val.toString () );
}
catch ( final NumberFormatException e )
{
return null;
}
}
return null;
}
private String getString ( final Configuration cfg, final String key )
{
final Object val = cfg.getProperties ().get ( key );
if ( val != null )
{
return val.toString ();
}
return null;
}
protected MailSettings createDefault ()
{
final MailSettings result = new MailSettings ();
return result;
}
@Override
public List<MenuEntry> getMainMenuEntries ( final HttpServletRequest request )
{
final List<MenuEntry> result = new LinkedList<> ();
if ( HttpConstraints.isCallAllowed ( METHOD_INDEX, request ) )
{
result.add ( new MenuEntry ( "Administration", 100, "Mail", 700, LinkTarget.createFromController ( METHOD_INDEX ), null, null ) );
}
return result;
}
@RequestMapping ( value = "/sendTest", method = RequestMethod.POST )
public ModelAndView sendTest ( @RequestParameter ( "testEmailReceiver" ) final String email, final Principal principal)
{
final Map<String, Object> model = new HashMap<> ();
try
{
String user = principal.getName ();
if ( principal instanceof UserInformationPrincipal )
{
final UserInformation userInfo = ( (UserInformationPrincipal)principal ).getUserInformation ();
final DatabaseDetails dbDetails = userInfo.getDetails ( DatabaseDetails.class );
if ( dbDetails != null && dbDetails.getEmail () != null )
{
user = dbDetails.getEmail ();
}
}
final String message = "This is an automated test message requested by: " + user;
final StringBuilder html = new StringBuilder ();
html.append ( "<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title>Package Drone | Account verification</title></head><body>" );
html.append ( "<p>" ).append ( message ).append ( "</p>" );
html.append ( "</body></html>" );
this.mailService.sendMessage ( email, "Test Mail", message, html.toString () );
}
catch ( final Throwable e )
{
logger.warn ( "Failed to send test e-mail", e );
return CommonController.createError ( "Test Mail", "Result", null, e, true );
}
return new ModelAndView ( "testSent", model );
}
}