Changing an email needs to be confirmed by receiving an email and responding to an URL send inside it
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 04 Mar 2010 19:37:58 +0100
changeset 233ecddc9f373bb
parent 232 4a94cefe6555
child 234 0a71b6bd786f
Changing an email needs to be confirmed by receiving an email and responding to an URL send inside it
freemarkerdor/pom.xml
freemarkerdor/src/main/java/cz/xelfi/quoridor/freemarkerdor/EmailService.java
freemarkerdor/src/main/java/cz/xelfi/quoridor/freemarkerdor/Requests.java
freemarkerdor/src/main/java/cz/xelfi/quoridor/freemarkerdor/UI.java
freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/Bundle.properties
freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/email.fmt
freemarkerdor/src/test/java/cz/xelfi/quoridor/freemarkerdor/ChangeEmailTest.java
pom.xml
     1.1 --- a/freemarkerdor/pom.xml	Tue Mar 02 17:09:06 2010 +0100
     1.2 +++ b/freemarkerdor/pom.xml	Thu Mar 04 19:37:58 2010 +0100
     1.3 @@ -13,6 +13,11 @@
     1.4    <url>http://maven.apache.org</url>
     1.5    <dependencies>
     1.6      <dependency>
     1.7 +        <groupId>javax.mail</groupId>
     1.8 +        <artifactId>mail</artifactId>
     1.9 +        <version>1.4.1</version>
    1.10 +    </dependency>
    1.11 +    <dependency>
    1.12        <groupId>${project.groupId}</groupId>
    1.13        <artifactId>webidor</artifactId>
    1.14        <version>${webidorVersion}</version>
    1.15 @@ -66,6 +71,12 @@
    1.16        <scope>test</scope>
    1.17      </dependency>
    1.18      <dependency>
    1.19 +        <groupId>org.netbeans.api</groupId>
    1.20 +        <artifactId>org-netbeans-modules-nbjunit</artifactId>
    1.21 +        <version>RELEASE65</version>
    1.22 +        <scope>test</scope>
    1.23 +    </dependency>
    1.24 +    <dependency>
    1.25        <groupId>freemarker</groupId>
    1.26        <artifactId>freemarker</artifactId>
    1.27        <version>2.3.8</version>
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/freemarkerdor/src/main/java/cz/xelfi/quoridor/freemarkerdor/EmailService.java	Thu Mar 04 19:37:58 2010 +0100
     2.3 @@ -0,0 +1,88 @@
     2.4 +/*
     2.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     2.6 + *
     2.7 + * The contents of this file are subject to the terms of either the GNU
     2.8 + * General Public License Version 2 only ("GPL") or the Common
     2.9 + * Development and Distribution License("CDDL") (collectively, the
    2.10 + * "License"). You may not use this file except in compliance with the
    2.11 + * License. You can obtain a copy of the License at
    2.12 + * http://www.netbeans.org/cddl-gplv2.html
    2.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    2.14 + * specific language governing permissions and limitations under the
    2.15 + * License.  When distributing the software, include this License Header
    2.16 + * Notice in each file and include the License file at
    2.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
    2.18 + * particular file as subject to the "Classpath" exception as provided
    2.19 + * by Sun in the GPL Version 2 section of the License file that
    2.20 + * accompanied this code. If applicable, add the following below the
    2.21 + * License Header, with the fields enclosed by brackets [] replaced by
    2.22 + * your own identifying information:
    2.23 + * "Portions Copyrighted [year] [name of copyright owner]"
    2.24 + *
    2.25 + * Contributor(s):
    2.26 + *
    2.27 + * Portions Copyrighted 2009 Jaroslav Tulach
    2.28 + */
    2.29 +
    2.30 +package cz.xelfi.quoridor.freemarkerdor;
    2.31 +
    2.32 +import java.io.IOException;
    2.33 +import java.util.Iterator;
    2.34 +import java.util.Properties;
    2.35 +import java.util.ServiceLoader;
    2.36 +import javax.mail.Message;
    2.37 +import javax.mail.MessagingException;
    2.38 +import javax.mail.Session;
    2.39 +import javax.mail.Transport;
    2.40 +import javax.mail.internet.InternetAddress;
    2.41 +import javax.mail.internet.MimeMessage;
    2.42 +import org.openide.util.Exceptions;
    2.43 +
    2.44 +/** Send emails.
    2.45 + *
    2.46 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    2.47 + */
    2.48 +public abstract class EmailService {
    2.49 +    private static EmailService DEFAULT;
    2.50 +    
    2.51 +    public abstract void sendEmail(
    2.52 +        String address,
    2.53 +        String subject,
    2.54 +        String text
    2.55 +    ) throws IOException;
    2.56 +
    2.57 +    public static synchronized EmailService getDefault() {
    2.58 +        if (DEFAULT == null) {
    2.59 +            Iterator<EmailService> it = ServiceLoader.load(EmailService.class).iterator();
    2.60 +            DEFAULT = it.hasNext() ? it.next() : new Impl();
    2.61 +        }
    2.62 +        return DEFAULT;
    2.63 +    }
    2.64 +
    2.65 +    private static class Impl extends EmailService {
    2.66 +        private final Session session;
    2.67 +        public Impl() {
    2.68 +            Properties props = System.getProperties();
    2.69 +            final String host = "mail.smtp.host";
    2.70 +            if (props.get(host) == null) {
    2.71 +                props.put(host,"localhost");
    2.72 +            }
    2.73 +            session = Session.getDefaultInstance(props, null);
    2.74 +        }
    2.75 +
    2.76 +        @Override
    2.77 +        public void sendEmail(String address, String subject, String text) throws IOException {
    2.78 +            try {
    2.79 +                MimeMessage message = new MimeMessage(session);
    2.80 +                message.setFrom(new InternetAddress("quoridor@xelfi.cz"));
    2.81 +                message.addRecipient(Message.RecipientType.TO, new InternetAddress(address));
    2.82 +                message.setSubject(subject);
    2.83 +                message.setText(text);
    2.84 +                Transport.send(message);
    2.85 +            } catch (MessagingException ex) {
    2.86 +                throw new IOException(ex);
    2.87 +            }
    2.88 +
    2.89 +        }
    2.90 +    }
    2.91 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/freemarkerdor/src/main/java/cz/xelfi/quoridor/freemarkerdor/Requests.java	Thu Mar 04 19:37:58 2010 +0100
     3.3 @@ -0,0 +1,81 @@
     3.4 +/*
     3.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3.6 + *
     3.7 + * The contents of this file are subject to the terms of either the GNU
     3.8 + * General Public License Version 2 only ("GPL") or the Common
     3.9 + * Development and Distribution License("CDDL") (collectively, the
    3.10 + * "License"). You may not use this file except in compliance with the
    3.11 + * License. You can obtain a copy of the License at
    3.12 + * http://www.netbeans.org/cddl-gplv2.html
    3.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    3.14 + * specific language governing permissions and limitations under the
    3.15 + * License.  When distributing the software, include this License Header
    3.16 + * Notice in each file and include the License file at
    3.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
    3.18 + * particular file as subject to the "Classpath" exception as provided
    3.19 + * by Sun in the GPL Version 2 section of the License file that
    3.20 + * accompanied this code. If applicable, add the following below the
    3.21 + * License Header, with the fields enclosed by brackets [] replaced by
    3.22 + * your own identifying information:
    3.23 + * "Portions Copyrighted [year] [name of copyright owner]"
    3.24 + *
    3.25 + * Contributor(s):
    3.26 + *
    3.27 + * Portions Copyrighted 2009 Jaroslav Tulach
    3.28 + */
    3.29 +
    3.30 +package cz.xelfi.quoridor.freemarkerdor;
    3.31 +
    3.32 +import com.sun.jersey.api.client.WebResource;
    3.33 +import com.sun.jersey.api.client.WebResource.Builder;
    3.34 +import java.net.URI;
    3.35 +import java.util.HashMap;
    3.36 +import java.util.Map;
    3.37 +import java.util.UUID;
    3.38 +import javax.ws.rs.GET;
    3.39 +import javax.ws.rs.Path;
    3.40 +import javax.ws.rs.PathParam;
    3.41 +import javax.ws.rs.Produces;
    3.42 +import javax.ws.rs.core.Cookie;
    3.43 +import javax.ws.rs.core.MediaType;
    3.44 +import javax.ws.rs.core.Response;
    3.45 +
    3.46 +/** Keeps "requests". A request is UUID that maps to a URL which can
    3.47 + * be executed later.
    3.48 + *
    3.49 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    3.50 + */
    3.51 +public final class Requests extends Object {
    3.52 +    private final Map<UUID,Builder> requests = new HashMap<UUID, Builder>();
    3.53 +    private final WebResource root;
    3.54 +
    3.55 +    Requests(WebResource path) {
    3.56 +        root = path;
    3.57 +    }
    3.58 +
    3.59 +    @GET
    3.60 +    @Path("{id}")
    3.61 +    @Produces(MediaType.TEXT_HTML)
    3.62 +    public Response redirect(@PathParam("id") String uuid) {
    3.63 +        UUID r = UUID.fromString(uuid);
    3.64 +        Builder web = requests.get(r);
    3.65 +        if (web == null) {
    3.66 +            return Response.status(Response.Status.NOT_FOUND).build();
    3.67 +        }
    3.68 +        return Response.ok(web.get(String.class)).build();
    3.69 +    }
    3.70 +
    3.71 +    boolean isVerified(String uuid) {
    3.72 +        return uuid != null && requests.containsKey(UUID.fromString(uuid));
    3.73 +    }
    3.74 +
    3.75 +    URI register(String login, WebResource request) {
    3.76 +        UUID uuid = UUID.randomUUID();
    3.77 +        WebResource callback = root.path(uuid.toString());
    3.78 +        Builder builder = request.queryParam("verified", uuid.toString()).
    3.79 +            cookie(Cookie.valueOf("login=" + login));
    3.80 +        requests.put(uuid, builder);
    3.81 +        return callback.getURI();
    3.82 +    }
    3.83 +
    3.84 +}
     4.1 --- a/freemarkerdor/src/main/java/cz/xelfi/quoridor/freemarkerdor/UI.java	Tue Mar 02 17:09:06 2010 +0100
     4.2 +++ b/freemarkerdor/src/main/java/cz/xelfi/quoridor/freemarkerdor/UI.java	Thu Mar 04 19:37:58 2010 +0100
     4.3 @@ -40,6 +40,7 @@
     4.4  import java.io.InputStream;
     4.5  import java.io.StringWriter;
     4.6  import java.net.URI;
     4.7 +import java.text.MessageFormat;
     4.8  import java.util.Date;
     4.9  import java.util.HashMap;
    4.10  import java.util.List;
    4.11 @@ -89,6 +90,8 @@
    4.12      }
    4.13      private static WebResource base;
    4.14      private static WebResource stat;
    4.15 +    private static WebResource web;
    4.16 +    private static Requests requests;
    4.17  
    4.18      @Context
    4.19      private HttpHeaders headers;
    4.20 @@ -98,10 +101,17 @@
    4.21      public UI() {
    4.22      }
    4.23  
    4.24 -    private Viewable checkLogin() {
    4.25 +    private String login() {
    4.26          Cookie cookie = headers.getCookies().get("login");
    4.27          if (cookie != null) {
    4.28 -            String id = cookie.getValue();
    4.29 +            return cookie.getValue();
    4.30 +        }
    4.31 +        return null;
    4.32 +    }
    4.33 +
    4.34 +    private Viewable checkLogin() {
    4.35 +        String id = login();
    4.36 +        if (id != null) {
    4.37              UserInfo us;
    4.38              try {
    4.39                  us = base.path("users").queryParam("loginID", id).
    4.40 @@ -342,23 +352,45 @@
    4.41          return viewable("index.fmt", got, args);
    4.42      }
    4.43  
    4.44 +    @Path("requests")
    4.45 +    public Requests getRequests() {
    4.46 +        if (requests == null) {
    4.47 +            requests = new Requests(web.path("requests"));
    4.48 +        }
    4.49 +        return requests;
    4.50 +    }
    4.51 +
    4.52  
    4.53      @GET
    4.54      @Path("options")
    4.55      public Response changeOptions(
    4.56          @QueryParam("email") String email,
    4.57 -        @QueryParam("language") String language
    4.58 -    ) {
    4.59 +        @QueryParam("language") String language,
    4.60 +        @QueryParam("verified") String verified
    4.61 +    ) throws IOException {
    4.62          Viewable v = checkLogin();
    4.63          if (v != null) {
    4.64              return Response.status(Response.Status.FORBIDDEN).entity(v).build();
    4.65          }
    4.66  
    4.67          if (email != null) {
    4.68 -            UserInfo ui = base.path("users/" + user.getId()).
    4.69 -                queryParam("loginID", uuid).
    4.70 -                queryParam("name", "email").
    4.71 -                queryParam("value", email).accept(MediaType.TEXT_XML).post(UserInfo.class);
    4.72 +            if (getRequests().isVerified(verified)) {
    4.73 +                UserInfo ui = base.path("users/" + user.getId()).
    4.74 +                    queryParam("loginID", uuid).
    4.75 +                    queryParam("name", "email").
    4.76 +                    queryParam("value", email).accept(MediaType.TEXT_XML).post(UserInfo.class);
    4.77 +            } else {
    4.78 +                WebResource request;
    4.79 +                request = web.path("options").queryParam("name", "email").queryParam("email", email);
    4.80 +                URI callback = getRequests().register(login(), request);
    4.81 +
    4.82 +                ResourceBundle rb = bundle(null);
    4.83 +                String subject = rb.getString("MSG_ChangeEmailSubject");
    4.84 +                String text = MessageFormat.format(rb.getString("MSG_ChangeEmailText"), user.getId(), callback);
    4.85 +                EmailService.getDefault().sendEmail(email, subject, text);
    4.86 +                return Response.ok(viewable("email.fmt", null)).build();
    4.87 +
    4.88 +            }
    4.89          }
    4.90  
    4.91          if (language != null) {
    4.92 @@ -490,6 +522,8 @@
    4.93  
    4.94          final String baseUri = "http://localhost:" + port + "/";
    4.95          final HttpServer server = HttpServerFactory.create(baseUri, rc);
    4.96 +        Client c3 = new Client();
    4.97 +        web = c3.resource(baseUri);
    4.98          server.start();
    4.99          System.out.println("Quoridor started at port " + port);
   4.100  
   4.101 @@ -504,24 +538,27 @@
   4.102          };
   4.103      }
   4.104  
   4.105 -    private Viewable viewable(String page, Document doc, Object... more) {
   4.106 +    private ResourceBundle bundle(Locale[] locale) {
   4.107          ResourceBundle rb = null;
   4.108          String lng = user == null ? null : user.getProperty("language"); // NOI18N
   4.109 -        Locale locale = null;
   4.110          if (lng != null) {
   4.111 -                try {
   4.112 -                    Locale l = new Locale(lng);
   4.113 -                    rb = ResourceBundle.getBundle("cz.xelfi.quoridor.freemarkerdor.UI.Bundle", l);
   4.114 -                    locale = l;
   4.115 -                } catch (MissingResourceException e) {
   4.116 -                    // OK
   4.117 +            try {
   4.118 +                Locale l = new Locale(lng);
   4.119 +                rb = ResourceBundle.getBundle("cz.xelfi.quoridor.freemarkerdor.UI.Bundle", l);
   4.120 +                if (locale != null) {
   4.121 +                    locale[0] = l;
   4.122                  }
   4.123 +            } catch (MissingResourceException e) {
   4.124 +                // OK
   4.125 +            }
   4.126          }
   4.127          if (rb == null) {
   4.128              for (Locale l : headers.getAcceptableLanguages()) {
   4.129                  try {
   4.130                      rb = ResourceBundle.getBundle("cz.xelfi.quoridor.freemarkerdor.UI.Bundle", l);
   4.131 -                    locale = l;
   4.132 +                    if (locale != null) {
   4.133 +                        locale[0] = l;
   4.134 +                    }
   4.135                      break;
   4.136                  } catch (MissingResourceException e) {
   4.137                      // OK
   4.138 @@ -530,8 +567,16 @@
   4.139          }
   4.140          if (rb == null) {
   4.141              rb = ResourceBundle.getBundle("cz.xelfi.quoridor.freemarkerdor.UI.Bundle", Locale.ENGLISH);
   4.142 -            locale = Locale.ENGLISH;
   4.143 +            if (locale != null) {
   4.144 +                locale[0] = Locale.ENGLISH;
   4.145 +            }
   4.146          }
   4.147 +        return rb;
   4.148 +    }
   4.149 +
   4.150 +    private Viewable viewable(String page, Document doc, Object... more) {
   4.151 +        Locale[] locale = new Locale[1];
   4.152 +        ResourceBundle rb = bundle(locale);
   4.153  
   4.154          Map<String,Object> map = new HashMap<String,Object>();
   4.155          class ConvertToDate extends HashMap<Object,Object> {
   4.156 @@ -542,7 +587,7 @@
   4.157              }
   4.158          }
   4.159  
   4.160 -        map.put("locale", locale.toString());
   4.161 +        map.put("locale", locale[0].toString());
   4.162          map.put("doc", doc);
   4.163          if (user != null) {
   4.164              map.put("user", user.getId());
     5.1 --- a/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/Bundle.properties	Tue Mar 02 17:09:06 2010 +0100
     5.2 +++ b/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/Bundle.properties	Thu Mar 04 19:37:58 2010 +0100
     5.3 @@ -85,3 +85,17 @@
     5.4  OPENINGS=Openings
     5.5  WHITE_WON=White won
     5.6  BLACK_WON=Black won
     5.7 +
     5.8 +MSG_ChangeEmailSubject=Quoridor Email Change
     5.9 +# {0} user id
    5.10 +# {1} request URL
    5.11 +MSG_ChangeEmailText=Dear Quoridor user!\n\
    5.12 + User {0} have requested change in email settings.\n\
    5.13 + \n\
    5.14 + To confirm change of the email address, please visit\n\
    5.15 + following link {1}\n\
    5.16 + \n\
    5.17 + Thank you for playing Quoridor!
    5.18 +EMAIL_SENT=Email Sent
    5.19 +EMAIL_SENT_TO=Email Sent to {0}
    5.20 +EMAIL_BACK=Return to games...
    5.21 \ No newline at end of file
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/email.fmt	Thu Mar 04 19:37:58 2010 +0100
     6.3 @@ -0,0 +1,16 @@
     6.4 +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
     6.5 +<html>
     6.6 +  <head>
     6.7 +    <title>${bundle.EMAIL_SENT}</title>
     6.8 +    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
     6.9 +  </head>
    6.10 +  <body bgcolor="white">
    6.11 +      <h3><a href="/">${bundle("EMAIL_SENT_TO", user)}</a></h3>
    6.12 +
    6.13 +      <a href="/">${bundle.EMAIL_BACK}</a>
    6.14 +
    6.15 +      <hr/>
    6.16 +      ${bundle("copyright", version)}
    6.17 +
    6.18 +  </body>
    6.19 +</html>
    6.20 \ No newline at end of file
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/freemarkerdor/src/test/java/cz/xelfi/quoridor/freemarkerdor/ChangeEmailTest.java	Thu Mar 04 19:37:58 2010 +0100
     7.3 @@ -0,0 +1,176 @@
     7.4 +/*
     7.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     7.6 + *
     7.7 + * The contents of this file are subject to the terms of either the GNU
     7.8 + * General Public License Version 2 only ("GPL") or the Common
     7.9 + * Development and Distribution License("CDDL") (collectively, the
    7.10 + * "License"). You may not use this file except in compliance with the
    7.11 + * License. You can obtain a copy of the License at
    7.12 + * http://www.netbeans.org/cddl-gplv2.html
    7.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    7.14 + * specific language governing permissions and limitations under the
    7.15 + * License.  When distributing the software, include this License Header
    7.16 + * Notice in each file and include the License file at
    7.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
    7.18 + * particular file as subject to the "Classpath" exception as provided
    7.19 + * by Sun in the GPL Version 2 section of the License file that
    7.20 + * accompanied this code. If applicable, add the following below the
    7.21 + * License Header, with the fields enclosed by brackets [] replaced by
    7.22 + * your own identifying information:
    7.23 + * "Portions Copyrighted [year] [name of copyright owner]"
    7.24 + *
    7.25 + * Contributor(s):
    7.26 + *
    7.27 + * Portions Copyrighted 2010 Jaroslav Tulach
    7.28 + */
    7.29 +
    7.30 +package cz.xelfi.quoridor.freemarkerdor;
    7.31 +
    7.32 +import com.sun.jersey.api.client.Client;
    7.33 +import com.sun.jersey.api.client.ClientResponse;
    7.34 +import com.sun.jersey.api.client.WebResource;
    7.35 +import com.sun.jersey.core.header.MediaTypes;
    7.36 +import com.sun.jersey.core.util.MultivaluedMapImpl;
    7.37 +import com.sun.net.httpserver.HttpServer;
    7.38 +import cz.xelfi.quoridor.webidor.resources.Quoridor;
    7.39 +import java.io.File;
    7.40 +import java.io.FileOutputStream;
    7.41 +import java.io.IOException;
    7.42 +import java.io.InputStream;
    7.43 +import java.net.URI;
    7.44 +import java.util.Locale;
    7.45 +import java.util.concurrent.Callable;
    7.46 +import java.util.regex.Matcher;
    7.47 +import java.util.regex.Pattern;
    7.48 +import javax.ws.rs.core.Cookie;
    7.49 +import javax.ws.rs.core.MediaType;
    7.50 +import javax.ws.rs.core.MultivaluedMap;
    7.51 +import javax.ws.rs.core.UriBuilder;
    7.52 +import org.junit.AfterClass;
    7.53 +import org.junit.Before;
    7.54 +import org.junit.BeforeClass;
    7.55 +import org.junit.Test;
    7.56 +import org.netbeans.junit.MockServices;
    7.57 +import static org.junit.Assert.*;
    7.58 +
    7.59 +/**
    7.60 + *
    7.61 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    7.62 + */
    7.63 +public class ChangeEmailTest extends Object {
    7.64 +    private static File dir;
    7.65 +    private static HttpServer stopAPI;
    7.66 +    private static HttpServer stopStatistics;
    7.67 +    private static Callable<Void> stop;
    7.68 +    private WebResource webResource;
    7.69 +    private WebResource apiResource;
    7.70 +    private WebResource statResource;
    7.71 +    private Client client;
    7.72 +
    7.73 +    public ChangeEmailTest() throws Exception {
    7.74 +    }
    7.75 +
    7.76 +    @BeforeClass
    7.77 +    public static void localeEnglish() throws Exception {
    7.78 +        Locale.setDefault(Locale.ENGLISH);
    7.79 +        dir = File.createTempFile("quoridor", ".dir");
    7.80 +        dir.delete();
    7.81 +        dir.mkdirs();
    7.82 +        System.setProperty("quoridor.dir", dir.getPath());
    7.83 +        stopAPI = Quoridor.start(7990);
    7.84 +        stopStatistics = Quoridor.start(7992);
    7.85 +        stop = UI.startServers(7991, "http://localhost:7990", "http://localhost:7992");
    7.86 +
    7.87 +        File passwd = new File(dir, "passwd");
    7.88 +        FileOutputStream os = new FileOutputStream(passwd);
    7.89 +        os.write("test=pes\nJarda=darda\n".getBytes("UTF-8"));
    7.90 +        os.close();
    7.91 +
    7.92 +        MockServices.setServices(MockEmailer.class);
    7.93 +    }
    7.94 +
    7.95 +    @Before
    7.96 +    public void setUp() throws Exception {
    7.97 +        MockEmailer.clear();
    7.98 +
    7.99 +        client = new Client();
   7.100 +        webResource = client.resource(new URI("http://localhost:7991/"));
   7.101 +        apiResource = client.resource(new URI("http://localhost:7990/"));
   7.102 +        statResource = client.resource(new URI("http://localhost:7992/"));
   7.103 +    }
   7.104 +
   7.105 +    @AfterClass
   7.106 +    public static void cleanUpAll() throws Exception {
   7.107 +        deleteRec(dir);
   7.108 +        if (stop != null) {
   7.109 +            stop.call();
   7.110 +        }
   7.111 +        if (stopAPI != null) {
   7.112 +            stopAPI.stop(0);
   7.113 +        }
   7.114 +        MockServices.setServices();
   7.115 +    }
   7.116 +
   7.117 +    static void deleteRec(File dir) throws IOException {
   7.118 +        if (dir == null) {
   7.119 +            return;
   7.120 +        }
   7.121 +        File[] arr = dir.listFiles();
   7.122 +        if (arr != null) {
   7.123 +            for (File f : arr) {
   7.124 +                deleteRec(f);
   7.125 +            }
   7.126 +        }
   7.127 +        dir.delete();
   7.128 +    }
   7.129 +
   7.130 +    @Test public void testChangeEmail() throws Exception {
   7.131 +        String logTest = apiResource.path("login").
   7.132 +            queryParam("name", "test").
   7.133 +            queryParam("password", "pes").
   7.134 +            accept(MediaType.TEXT_PLAIN).
   7.135 +            put(String.class);
   7.136 +        ClientResponse res = webResource.path("options").
   7.137 +            queryParam("email", "xxx@xxx.cz").
   7.138 +            cookie(Cookie.valueOf("login=" + logTest)).
   7.139 +            accept("text/html").
   7.140 +            get(ClientResponse.class);
   7.141 +        final String txt = res.getEntity(String.class);
   7.142 +
   7.143 +        assertEquals("Emails sent to", "xxx@xxx.cz", MockEmailer.address);
   7.144 +
   7.145 +        int urlIndex = MockEmailer.text.indexOf(webResource.getURI().toString());
   7.146 +        assertTrue("Found in " + MockEmailer.text, urlIndex >= 0);
   7.147 +
   7.148 +        int endIndex = urlIndex;
   7.149 +        while (!Character.isWhitespace(MockEmailer.text.charAt(endIndex))) {
   7.150 +            endIndex++;
   7.151 +        }
   7.152 +        final String url = MockEmailer.text.substring(urlIndex, endIndex);
   7.153 +        WebResource requestURL = client.resource(url);
   7.154 +
   7.155 +        String response = requestURL.accept("text/html").get(String.class);
   7.156 +        Pattern p = Pattern.compile("<input *type=\"text\" *name=\"email\" *value='xxx@xxx.cz'");
   7.157 +        assertTrue("New email found" + response, p.matcher(response).find());
   7.158 +    }
   7.159 +
   7.160 +    public static final class MockEmailer extends EmailService {
   7.161 +        static String address;
   7.162 +        static String subject;
   7.163 +        static String text;
   7.164 +
   7.165 +        @Override
   7.166 +        public void sendEmail(String address, String subject, String text) throws IOException {
   7.167 +            assertNull("No email sent yet", MockEmailer.address);
   7.168 +            MockEmailer.address = address;
   7.169 +            MockEmailer.subject = subject;
   7.170 +            MockEmailer.text = text;
   7.171 +        }
   7.172 +
   7.173 +        public static void clear() {
   7.174 +            MockEmailer.address = null;
   7.175 +            MockEmailer.subject = null;
   7.176 +            MockEmailer.text = null;
   7.177 +        }
   7.178 +    }
   7.179 +}
     8.1 --- a/pom.xml	Tue Mar 02 17:09:06 2010 +0100
     8.2 +++ b/pom.xml	Thu Mar 04 19:37:58 2010 +0100
     8.3 @@ -37,7 +37,7 @@
     8.4        <quoridorVersion>1.1</quoridorVersion>
     8.5        <webidorVersion>1.15</webidorVersion>
     8.6        <visidorVersion>1.0-SNAPSHOT</visidorVersion>
     8.7 -      <freemarkerVersion>1.58</freemarkerVersion>
     8.8 +      <freemarkerVersion>1.59</freemarkerVersion>
     8.9        <emailerVersion>1.0</emailerVersion>
    8.10        <statisticsVersion>1.8</statisticsVersion>
    8.11    </properties>