001/*
002 * (C) Copyright 2011-2016 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     tdelprat, jcarsique
018 *
019 */
020
021package org.nuxeo.wizard;
022
023import static org.nuxeo.common.Environment.NUXEO_DATA_DIR;
024import static org.nuxeo.launcher.config.ConfigurationGenerator.DB_EXCLUDE_CHECK_LIST;
025import static org.nuxeo.launcher.config.ConfigurationGenerator.DB_SECONDARY_LIST;
026import static org.nuxeo.launcher.config.ConfigurationGenerator.INSTALL_AFTER_RESTART;
027import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_BIND_ADDRESS;
028import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_DB_HOST;
029import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_DB_NAME;
030import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_DB_PORT;
031import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_DB_PWD;
032import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_DB_USER;
033import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_MONGODB_NAME;
034import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_MONGODB_SERVER;
035import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_TEMPLATE_DBNAME;
036import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_TEMPLATE_DBSECONDARY_NAME;
037import static org.nuxeo.launcher.config.ConfigurationGenerator.PARAM_WIZARD_DONE;
038
039import java.io.File;
040import java.io.IOException;
041import java.lang.reflect.Method;
042import java.net.InetAddress;
043import java.net.URLEncoder;
044import java.sql.SQLException;
045import java.util.ArrayList;
046import java.util.Enumeration;
047import java.util.HashMap;
048import java.util.Hashtable;
049import java.util.List;
050import java.util.Map;
051
052import javax.naming.AuthenticationException;
053import javax.naming.NameNotFoundException;
054import javax.naming.NamingEnumeration;
055import javax.naming.NamingException;
056import javax.naming.directory.Attributes;
057import javax.naming.directory.DirContext;
058import javax.naming.directory.InitialDirContext;
059import javax.naming.directory.SearchControls;
060import javax.naming.directory.SearchResult;
061import javax.servlet.ServletException;
062import javax.servlet.http.HttpServlet;
063import javax.servlet.http.HttpServletRequest;
064import javax.servlet.http.HttpServletResponse;
065
066import org.apache.commons.codec.binary.Base64;
067import org.apache.commons.logging.Log;
068import org.apache.commons.logging.LogFactory;
069import org.nuxeo.launcher.commons.DatabaseDriverException;
070import org.nuxeo.launcher.config.ConfigurationException;
071import org.nuxeo.launcher.config.ConfigurationGenerator;
072import org.nuxeo.wizard.context.Context;
073import org.nuxeo.wizard.context.ParamCollector;
074import org.nuxeo.wizard.download.DownloadablePackageOptions;
075import org.nuxeo.wizard.download.PackageDownloader;
076import org.nuxeo.wizard.helpers.ConnectRegistrationHelper;
077import org.nuxeo.wizard.helpers.IPValidator;
078import org.nuxeo.wizard.helpers.NumberValidator;
079import org.nuxeo.wizard.helpers.PackageDownloaderHelper;
080import org.nuxeo.wizard.nav.Page;
081import org.nuxeo.wizard.nav.SimpleNavigationHandler;
082
083/**
084 * Main entry point : find the right handler and start jsp rendering
085 *
086 * @author Tiry (tdelprat@nuxeo.com)
087 * @since 5.4.2
088 */
089public class RouterServlet extends HttpServlet {
090
091    private static final long serialVersionUID = 1L;
092
093    protected static Log log = LogFactory.getLog(RouterServlet.class);
094
095    protected SimpleNavigationHandler navHandler = SimpleNavigationHandler.instance();
096
097    public static final String CONNECT_TOKEN_KEY = "ConnectRegistrationToken";
098
099    protected String getAction(HttpServletRequest req) {
100        String uri = req.getRequestURI();
101
102        int idx = uri.indexOf("?");
103        if (idx > 0) {
104            uri = uri.substring(0, idx - 1);
105        }
106        String action = uri.replace(req.getContextPath() + "/router/", "");
107        if (action.startsWith("/")) {
108            action = action.substring(1);
109        }
110        return action;
111    }
112
113    @Override
114    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
115        // process action
116        handleAction(getAction(req), req, resp);
117    }
118
119    @Override
120    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
121        // store posted data
122        req.setCharacterEncoding("UTF-8");
123        Context.instance(req).getCollector().collectConfigurationParams(req);
124        doGet(req, resp);
125    }
126
127    protected Method findhandler(Page currentPage, String verb) {
128        String methodName = "handle" + currentPage.getAction() + verb;
129        Method method = null;
130        try {
131            method = this.getClass().getMethod(methodName, Page.class, HttpServletRequest.class,
132                    HttpServletResponse.class);
133        } catch (Exception e) {
134            // fall back to default Handler lookup
135            methodName = "handleDefault" + verb;
136            try {
137                method = this.getClass().getMethod(methodName, Page.class, HttpServletRequest.class,
138                        HttpServletResponse.class);
139            } catch (Exception e2) {
140                log.error("Unable to resolve default handler for " + verb, e);
141            }
142        }
143        return method;
144    }
145
146    protected void handleAction(String action, HttpServletRequest req, HttpServletResponse resp)
147            throws ServletException, IOException {
148        // locate page
149        Page currentPage = navHandler.getCurrentPage(action);
150        if (currentPage == null) {
151            resp.sendError(404, "Action " + action + " is not supported");
152            return;
153        }
154
155        // find action handler
156        Method handler = findhandler(currentPage, req.getMethod());
157        if (handler == null) {
158            resp.sendError(500, "No handler found for " + action);
159            return;
160        }
161
162        // execute handler => triggers rendering
163        try {
164            handler.invoke(this, currentPage, req, resp);
165        } catch (Exception e) {
166            log.error("Error during handler execution", e);
167            req.setAttribute("error", e);
168            req.getRequestDispatcher("/error.jsp").forward(req, resp);
169        }
170    }
171
172    // default handlers
173
174    public void handleDefaultGET(Page currentPage, HttpServletRequest req, HttpServletResponse resp)
175            throws ServletException, IOException {
176        currentPage.dispatchToJSP(req, resp);
177    }
178
179    public void handleDefaultPOST(Page currentPage, HttpServletRequest req, HttpServletResponse resp)
180            throws ServletException, IOException {
181        // XXX validate data
182        currentPage.next().dispatchToJSP(req, resp, true);
183    }
184
185    // custom handlers
186
187    public void handleConnectGET(Page currentPage, HttpServletRequest req, HttpServletResponse resp)
188            throws ServletException, IOException {
189        req.setAttribute("popupUrl", req.getContextPath() + "/ConnectCallback?action=display");
190        handleDefaultGET(currentPage, req, resp);
191    }
192
193    public void handleConnectCallbackGET(Page currentPage, HttpServletRequest req, HttpServletResponse resp)
194            throws ServletException, IOException {
195        Context ctx = Context.instance(req);
196        String token = req.getParameter(CONNECT_TOKEN_KEY);
197        String action = req.getParameter("action");
198        String targetNav = null;
199
200        if (action == null || action.isEmpty()) {
201            action = "skip";
202        }
203        if (action.equals("register") && (token == null || token.isEmpty())) {
204            action = "skip";
205        }
206
207        if ("register".equals(action)) {
208            // store the registration info
209            Map<String, String> connectMap = new HashMap<>();
210            Context context = Context.instance(req);
211            if (token != null) {
212                String tokenData = new String(Base64.decodeBase64(token));
213                String[] tokenDataLines = tokenData.split("\n");
214                for (String line : tokenDataLines) {
215                    String[] parts = line.split(":");
216                    if (parts.length > 1) {
217                        connectMap.put(parts[0], parts[1]);
218                    }
219                }
220                context.storeConnectMap(connectMap);
221            }
222
223            // Save CLID
224            if (context.isConnectRegistrationDone()) {
225                // save Connect registration
226                ConnectRegistrationHelper.saveConnectRegistrationFile(context);
227            }
228
229            // deactivate the confirm form
230            SimpleNavigationHandler.instance().deactivatePage("ConnectFinish");
231            // go to the next page
232            targetNav = currentPage.next().getAction();
233        } else if ("skip".equals(action)) {
234            // activate the confirm form
235            SimpleNavigationHandler.instance().activatePage("ConnectFinish");
236            // go to it
237            targetNav = currentPage.next().getAction();
238        } else if ("prev".equals(action)) {
239            targetNav = currentPage.prev().prev().getAction();
240        } else if ("display".equals(action)) {
241            // compute CB url
242            String cbUrl = req.getRequestURL().toString();
243            cbUrl = cbUrl.replace("/router/" + currentPage.getAction(), "/ConnectCallback?cb=yes");
244            // In order to avoid any issue with badly configured reverse proxies
245            // => get url from the client side
246            if (ctx.getBaseUrl() != null) {
247                cbUrl = ctx.getBaseUrl() + "ConnectCallback?cb=yes";
248            }
249            cbUrl = URLEncoder.encode(cbUrl, "UTF-8");
250
251            String redirect = ctx.getCollector().getConfigurationParam("org.nuxeo.connect.url",
252                    "https://connect.nuxeo.com/nuxeo/site/") + "../../register/#/embedded?wizardCallbackUrl=" + cbUrl
253                    + "&pkg=" + ctx.getDistributionKey();
254
255            resp.sendRedirect(redirect);
256            return;
257        }
258
259        String targetUrl = req.getContextPath() + "/" + targetNav;
260        req.setAttribute("targetUrl", targetUrl);
261        handleDefaultGET(currentPage, req, resp);
262    }
263
264    public void handleConnectFinishGET(Page currentPage, HttpServletRequest req, HttpServletResponse resp)
265            throws ServletException, IOException {
266        // get the connect Token and decode associated infos
267        String token = req.getParameter(CONNECT_TOKEN_KEY);
268        Map<String, String> connectMap = new HashMap<>();
269        if (token != null) {
270            String tokenData = new String(Base64.decodeBase64(token));
271            String[] tokenDataLines = tokenData.split("\n");
272            for (String line : tokenDataLines) {
273                String[] parts = line.split(":");
274                if (parts.length > 1) {
275                    connectMap.put(parts[0], parts[1]);
276                }
277            }
278            Context.instance(req).storeConnectMap(connectMap);
279        }
280        handleDefaultGET(currentPage, req, resp);
281    }
282
283    public void handleDBPOST(Page currentPage, HttpServletRequest req, HttpServletResponse resp)
284            throws ServletException, IOException {
285        Context ctx = Context.instance(req);
286        ParamCollector collector = ctx.getCollector();
287
288        String templateDbName = collector.getConfigurationParam(PARAM_TEMPLATE_DBNAME);
289        String templateDbNoSQLName = collector.getConfigurationParam(PARAM_TEMPLATE_DBSECONDARY_NAME);
290        if ("true".equals(req.getParameter("refresh"))) {
291            collector.changeDBTemplate(templateDbName);
292            collector.changeDBTemplate(templateDbNoSQLName);
293            collector.removeDbKeys();
294            currentPage.dispatchToJSP(req, resp);
295            return;
296        }
297
298        // Check relational database
299        if (!DB_EXCLUDE_CHECK_LIST.contains(templateDbName)) {
300            if (collector.getConfigurationParam(PARAM_DB_NAME).isEmpty()) {
301                ctx.trackError(PARAM_DB_NAME, "error.dbname.required");
302            }
303            if (collector.getConfigurationParam(PARAM_DB_USER).isEmpty()) {
304                ctx.trackError(PARAM_DB_USER, "error.dbuser.required");
305            }
306            if (collector.getConfigurationParam(PARAM_DB_PWD).isEmpty()) {
307                ctx.trackError(PARAM_DB_PWD, "error.dbpassword.required");
308            }
309            if (collector.getConfigurationParam(PARAM_DB_HOST).isEmpty()) {
310                ctx.trackError(PARAM_DB_HOST, "error.dbhost.required");
311            }
312            if (collector.getConfigurationParam(PARAM_DB_PORT).isEmpty()) {
313                ctx.trackError(PARAM_DB_PORT, "error.dbport.required");
314            } else {
315                if (!NumberValidator.validate(collector.getConfigurationParam(PARAM_DB_PORT))) {
316                    ctx.trackError(PARAM_DB_PORT, "error.invalid.port");
317                } else {
318                    int dbPort = Integer.parseInt(collector.getConfigurationParam(PARAM_DB_PORT));
319                    if (dbPort < 1024 || dbPort > 65536) {
320                        ctx.trackError(PARAM_DB_PORT, "error.invalid.port");
321                    }
322                }
323            }
324            ConfigurationGenerator cg = collector.getConfigurationGenerator();
325            try {
326                cg.checkDatabaseConnection(templateDbName, collector.getConfigurationParam(PARAM_DB_NAME),
327                        collector.getConfigurationParam(PARAM_DB_USER), collector.getConfigurationParam(PARAM_DB_PWD),
328                        collector.getConfigurationParam(PARAM_DB_HOST), collector.getConfigurationParam(PARAM_DB_PORT));
329            } catch (DatabaseDriverException e) {
330                ctx.trackError(PARAM_DB_NAME, "error.db.driver.notfound");
331                log.warn(e);
332            } catch (SQLException e) {
333                ctx.trackError(PARAM_DB_NAME, "error.db.connection");
334                log.warn(e);
335            }
336        }
337
338        // Check MongoDB settings
339        if ("mongodb".equals(templateDbName)) {
340            if (collector.getConfigurationParam(PARAM_MONGODB_NAME).isEmpty()) {
341                ctx.trackError(PARAM_MONGODB_NAME, "error.dbname.required");
342            }
343            if (collector.getConfigurationParam(PARAM_MONGODB_SERVER).isEmpty()) {
344                ctx.trackError(PARAM_MONGODB_SERVER, "error.dburi.required");
345            }
346        }
347
348        if (ctx.hasErrors()) {
349            currentPage.dispatchToJSP(req, resp);
350        } else {
351            currentPage.next().dispatchToJSP(req, resp, true);
352        }
353    }
354
355    public void handleUserPOST(Page currentPage, HttpServletRequest req, HttpServletResponse resp)
356            throws ServletException, IOException {
357        Context ctx = Context.instance(req);
358        ParamCollector collector = ctx.getCollector();
359
360        String refreshParam = req.getParameter("refresh");
361        String directoryType = collector.getConfigurationParam("nuxeo.directory.type");
362
363        if ("true".equals(refreshParam)) {
364            currentPage.dispatchToJSP(req, resp);
365            return;
366        }
367
368        if ("checkNetwork".equals(refreshParam) || "checkAuth".equals(refreshParam)
369                || "checkUserLdapParam".equals(refreshParam) || "checkGroupLdapParam".equals(refreshParam)) {
370            try {
371                if ("checkNetwork".equals(refreshParam)) {
372                    bindLdapConnection(collector, false);
373                    ctx.trackInfo("nuxeo.ldap.url", "info.host.found");
374                } else if ("checkAuth".equals(refreshParam)) {
375                    bindLdapConnection(collector, true);
376                    ctx.trackInfo("nuxeo.ldap.auth", "info.auth.success");
377                } else {
378                    DirContext dirContext = new InitialDirContext(getContextEnv(collector, true));
379                    String searchScope;
380                    String searchBaseDn;
381                    String searchClass;
382                    String searchFilter;
383                    if ("checkUserLdapParam".equals(refreshParam)) {
384                        searchBaseDn = collector.getConfigurationParam("nuxeo.ldap.user.searchBaseDn");
385                        searchScope = collector.getConfigurationParam("nuxeo.ldap.user.searchScope");
386                        searchClass = collector.getConfigurationParam("nuxeo.ldap.user.searchClass");
387                        searchFilter = collector.getConfigurationParam("nuxeo.ldap.user.searchFilter");
388                    } else {
389                        searchBaseDn = collector.getConfigurationParam("nuxeo.ldap.group.searchBaseDn");
390                        searchScope = collector.getConfigurationParam("nuxeo.ldap.group.searchScope");
391                        searchFilter = collector.getConfigurationParam("nuxeo.ldap.group.searchFilter");
392                        searchClass = "";
393                    }
394
395                    SearchControls scts = new SearchControls();
396                    if ("onelevel".equals(searchScope)) {
397                        scts.setSearchScope(SearchControls.ONELEVEL_SCOPE);
398                    } else {
399                        scts.setSearchScope(SearchControls.SUBTREE_SCOPE);
400                    }
401                    String filter = String.format("(&(%s)(objectClass=%s))",
402                            searchFilter.isEmpty() ? "objectClass=*" : searchFilter,
403                            searchClass.isEmpty() ? "*" : searchClass);
404                    NamingEnumeration<SearchResult> results;
405                    try {
406                        results = dirContext.search(searchBaseDn, filter, scts);
407                        if (!results.hasMore()) {
408                            ctx.trackError("nuxeo.ldap.search", "error.ldap.noresult");
409                        } else {
410                            SearchResult result = results.next();
411                            if (searchBaseDn.equalsIgnoreCase(result.getNameInNamespace()) && results.hasMore()) {
412                                // try not to display the root of the search
413                                // base DN
414                                result = results.next();
415                            }
416                            ctx.trackInfo("dn", result.getNameInNamespace());
417                            Attributes attributes = result.getAttributes();
418                            NamingEnumeration<String> ids = attributes.getIDs();
419                            String id;
420                            StringBuilder sb;
421                            while (ids.hasMore()) {
422                                id = ids.next();
423                                NamingEnumeration<?> values = attributes.get(id).getAll();
424                                sb = new StringBuilder();
425                                while (values.hasMore()) {
426                                    sb.append(values.next()).append(" , ");
427                                }
428                                ctx.trackInfo(id, sb.substring(0, sb.length() - 3));
429                            }
430                        }
431                    } catch (NameNotFoundException e) {
432                        ctx.trackError("nuxeo.ldap.search", "error.ldap.searchBaseDn");
433                        log.warn(e);
434                    }
435                    dirContext.close();
436                }
437            } catch (AuthenticationException e) {
438                ctx.trackError("nuxeo.ldap.auth", "error.auth.failed");
439                log.warn(e);
440            } catch (NamingException e) {
441                ctx.trackError("nuxeo.ldap.url", "error.host.not.found");
442                log.warn(e);
443            }
444        }
445
446        // Form submit
447        if (!"default".equals(directoryType) && refreshParam.isEmpty()) {
448            // first check bind to LDAP server
449            try {
450                bindLdapConnection(collector, true);
451            } catch (NamingException e) {
452                ctx.trackError("nuxeo.ldap.auth", "error.ldap.bind.failed");
453                log.warn(e);
454            }
455
456            // then check mandatory fields
457            if (collector.getConfigurationParam("nuxeo.ldap.user.searchBaseDn").isEmpty()) {
458                ctx.trackError("nuxeo.ldap.user.searchBaseDn", "error.user.searchBaseDn.required");
459            }
460            if (collector.getConfigurationParam("nuxeo.ldap.user.mapping.rdn").isEmpty()) {
461                ctx.trackError("nuxeo.ldap.user.mapping.rdn", "error.user.rdn.required");
462            }
463            if (collector.getConfigurationParam("nuxeo.ldap.user.mapping.username").isEmpty()) {
464                ctx.trackError("nuxeo.ldap.user.mapping.username", "error.user.username.required");
465            }
466            if (collector.getConfigurationParam("nuxeo.ldap.user.mapping.password").isEmpty()) {
467                ctx.trackError("nuxeo.ldap.user.mapping.password", "error.user.password.required");
468            }
469            if (collector.getConfigurationParam("nuxeo.ldap.user.mapping.firstname").isEmpty()) {
470                ctx.trackError("nuxeo.ldap.user.mapping.firstname", "error.user.firstname.required");
471            }
472            if (collector.getConfigurationParam("nuxeo.ldap.user.mapping.lastname").isEmpty()) {
473                ctx.trackError("nuxeo.ldap.user.mapping.lastname", "error.user.lastname.required");
474            }
475            String userGroupStorage = collector.getConfigurationParam("nuxeo.user.group.storage");
476            if (!"userLdapOnly".equals(userGroupStorage) && !"multiUserSqlGroup".equals(userGroupStorage)) {
477                if (collector.getConfigurationParam("nuxeo.ldap.group.searchBaseDn").isEmpty()) {
478                    ctx.trackError("nuxeo.ldap.group.searchBaseDn", "error.group.searchBaseDn.required");
479                }
480                if (collector.getConfigurationParam("nuxeo.ldap.group.mapping.rdn").isEmpty()) {
481                    ctx.trackError("nuxeo.ldap.group.mapping.rdn", "error.group.rdn.required");
482                }
483                if (collector.getConfigurationParam("nuxeo.ldap.group.mapping.name").isEmpty()) {
484                    ctx.trackError("nuxeo.ldap.group.mapping.name", "error.group.name.required");
485                }
486            }
487            if ("true".equals(collector.getConfigurationParam("nuxeo.user.emergency.enable"))) {
488                if (collector.getConfigurationParam("nuxeo.user.emergency.username").isEmpty()) {
489                    ctx.trackError("nuxeo.user.emergency.username", "error.emergency.username.required");
490                }
491                if (collector.getConfigurationParam("nuxeo.user.emergency.password").isEmpty()) {
492                    ctx.trackError("nuxeo.user.emergency.password", "error.emergency.password.required");
493                }
494            }
495        }
496
497        if (ctx.hasErrors() || ctx.hasInfos()) {
498            currentPage.dispatchToJSP(req, resp);
499        } else {
500            currentPage.next().dispatchToJSP(req, resp, true);
501        }
502    }
503
504    private Hashtable<Object, Object> getContextEnv(ParamCollector collector, boolean checkAuth) {
505        String ldapUrl = collector.getConfigurationParam("nuxeo.ldap.url");
506        String ldapBindDn = collector.getConfigurationParam("nuxeo.ldap.binddn");
507        String ldapBindPassword = collector.getConfigurationParam("nuxeo.ldap.bindpassword");
508        ConfigurationGenerator cg = collector.getConfigurationGenerator();
509        return cg.getContextEnv(ldapUrl, ldapBindDn, ldapBindPassword, checkAuth);
510    }
511
512    private void bindLdapConnection(ParamCollector collector, boolean authenticate) throws NamingException {
513        ConfigurationGenerator cg = collector.getConfigurationGenerator();
514        cg.checkLdapConnection(getContextEnv(collector, authenticate));
515    }
516
517    public void handleSmtpPOST(Page currentPage, HttpServletRequest req, HttpServletResponse resp)
518            throws ServletException, IOException {
519
520        Context ctx = Context.instance(req);
521        ParamCollector collector = ctx.getCollector();
522
523        if (collector.getConfigurationParam("mail.transport.auth").equals("true")) {
524            if (collector.getConfigurationParam("mail.transport.user").isEmpty()) {
525                ctx.trackError("mail.transport.user", "error.mail.transport.user.required");
526            }
527            if (collector.getConfigurationParam("mail.transport.password").isEmpty()) {
528                ctx.trackError("mail.transport.password", "error.mail.transport.password.required");
529            }
530        }
531
532        if (!collector.getConfigurationParam("mail.transport.port").isEmpty()) {
533            if (!NumberValidator.validate(collector.getConfigurationParam("mail.transport.port"))) {
534                ctx.trackError("mail.transport.port", "error.mail.transport.port.mustbeanumber");
535            }
536        }
537
538        if (ctx.hasErrors()) {
539            currentPage.dispatchToJSP(req, resp);
540        } else {
541            currentPage.next().dispatchToJSP(req, resp, true);
542        }
543    }
544
545    public void handleRecapPOST(Page currentPage, HttpServletRequest req, HttpServletResponse resp)
546            throws ServletException, IOException {
547        Context ctx = Context.instance(req);
548        ParamCollector collector = ctx.getCollector();
549        ConfigurationGenerator cg = collector.getConfigurationGenerator();
550
551        // Mark package selection done
552        PackageDownloaderHelper.markPackageSelectionDone(ctx);
553
554        Map<String, String> changedParameters = collector.getConfigurationParams();
555        changedParameters.put(PARAM_WIZARD_DONE, "true");
556        try {
557            // save config
558            cg.saveFilteredConfiguration(changedParameters);
559
560            // // => page will trigger the restart
561            // new Page("", "reStarting.jsp").dispatchToJSP(req, resp);
562            currentPage.next().dispatchToJSP(req, resp, true);
563        } catch (ConfigurationException e) {
564            log.error("Could not save wizard parameters.", e);
565            currentPage.dispatchToJSP(req, resp);
566        }
567    }
568
569    public void handleGeneralPOST(Page currentPage, HttpServletRequest req, HttpServletResponse resp)
570            throws ServletException, IOException {
571        Context ctx = Context.instance(req);
572        ParamCollector collector = ctx.getCollector();
573        String bindAddress = collector.getConfigurationParamValue(PARAM_BIND_ADDRESS);
574        if (bindAddress != null && !bindAddress.isEmpty()) {
575            if (!IPValidator.validate(bindAddress)) {
576                ctx.trackError(PARAM_BIND_ADDRESS, "error.invalid.ip");
577            }
578            try {
579                InetAddress inetAddress = ConfigurationGenerator.getBindAddress(bindAddress);
580                ConfigurationGenerator.checkAddressReachable(inetAddress);
581            } catch (ConfigurationException e) {
582                ctx.trackError(PARAM_BIND_ADDRESS, "error.already.used.ip");
583            }
584        }
585
586        if (ctx.hasErrors()) {
587            currentPage.dispatchToJSP(req, resp);
588        } else {
589            currentPage.next().dispatchToJSP(req, resp, true);
590        }
591    }
592
593    public void handleHomeGET(Page currentPage, HttpServletRequest req, HttpServletResponse resp)
594            throws ServletException, IOException {
595        Context ctx = Context.instance(req);
596        if (PackageDownloaderHelper.isPackageSelectionDone(ctx)) {
597            navHandler.deactivatePage("PackagesSelection");
598            navHandler.deactivatePage("PackagesDownload");
599            navHandler.activatePage("PackagesSelectionDone");
600        }
601        handleDefaultGET(currentPage, req, resp);
602    }
603
604    public void handleHomePOST(Page currentPage, HttpServletRequest req, HttpServletResponse resp)
605            throws ServletException, IOException {
606        String baseUrl = req.getParameter("baseUrl");
607        if (baseUrl != null && !baseUrl.isEmpty()) {
608            if (baseUrl.endsWith("Home")) {
609                baseUrl = baseUrl.substring(0, baseUrl.length() - 4);
610                Context.instance(req).setBaseUrl(baseUrl);
611            }
612        }
613
614        String browserInternetAccess = req.getParameter("browserInternetAccess");
615        if ("true".equals(browserInternetAccess)) {
616            Context.instance(req).setBrowserInternetAccess(true);
617            SimpleNavigationHandler.instance().deactivatePage("NetworkBlocked");
618            SimpleNavigationHandler.instance().activatePage("Connect");
619        } else {
620            Context.instance(req).setBrowserInternetAccess(false);
621            SimpleNavigationHandler.instance().activatePage("NetworkBlocked");
622            SimpleNavigationHandler.instance().deactivatePage("Connect");
623        }
624
625        currentPage.next().dispatchToJSP(req, resp, true);
626    }
627
628    public void handleProxyPOST(Page currentPage, HttpServletRequest req, HttpServletResponse resp)
629            throws ServletException, IOException {
630        Context ctx = Context.instance(req);
631        ParamCollector collector = ctx.getCollector();
632        String proxyType = collector.getConfigurationParamValue("nuxeo.http.proxy.type");
633        if ("none".equals(proxyType)) {
634            collector.addConfigurationParam("nuxeo.http.proxy.type", null);
635            collector.addConfigurationParam("nuxeo.http.proxy.login", null);
636            collector.addConfigurationParam("nuxeo.http.proxy.password", null);
637            collector.addConfigurationParam("nuxeo.http.proxy.host", null);
638            collector.addConfigurationParam("nuxeo.http.proxy.port", null);
639            collector.addConfigurationParam("nuxeo.http.proxy.ntml.host", null);
640            collector.addConfigurationParam("nuxeo.http.proxy.ntml.domain", null);
641            if (!PackageDownloaderHelper.isPackageSelectionDone(ctx)) {
642                PackageDownloader.instance().setProxy(null, 0, null, null, null, null);
643            }
644        } else {
645            if (!NumberValidator.validate(collector.getConfigurationParam("nuxeo.http.proxy.port"))) {
646                ctx.trackError("nuxeo.http.proxy.port", "error.nuxeo.http.proxy.port");
647            }
648            if (collector.getConfigurationParam("nuxeo.http.proxy.host").isEmpty()) {
649                ctx.trackError("nuxeo.http.proxy.host", "error.nuxeo.http.proxy.emptyHost");
650            }
651            if ("anonymous".equals(proxyType)) {
652                collector.addConfigurationParam("nuxeo.http.proxy.login", null);
653                collector.addConfigurationParam("nuxeo.http.proxy.password", null);
654                collector.addConfigurationParam("nuxeo.http.proxy.ntml.host", null);
655                collector.addConfigurationParam("nuxeo.http.proxy.ntml.domain", null);
656
657                if (!ctx.hasErrors()) {
658                    if (!PackageDownloaderHelper.isPackageSelectionDone(ctx)) {
659                        PackageDownloader.instance().setProxy(
660                                collector.getConfigurationParamValue("nuxeo.http.proxy.host"),
661                                Integer.parseInt(collector.getConfigurationParamValue("nuxeo.http.proxy.port")), null,
662                                null, null, null);
663                    }
664                }
665            } else {
666                if (collector.getConfigurationParam("nuxeo.http.proxy.login").isEmpty()) {
667                    ctx.trackError("nuxeo.http.proxy.login", "error.nuxeo.http.proxy.emptyLogin");
668                } else {
669                    if (!ctx.hasErrors()) {
670                        if (!PackageDownloaderHelper.isPackageSelectionDone(ctx)) {
671                            PackageDownloader.instance().setProxy(
672                                    collector.getConfigurationParamValue("nuxeo.http.proxy.host"),
673                                    Integer.parseInt(collector.getConfigurationParamValue("nuxeo.http.proxy.port")),
674                                    collector.getConfigurationParamValue("nuxeo.http.proxy.login"),
675                                    collector.getConfigurationParamValue("nuxeo.http.proxy.password"),
676                                    collector.getConfigurationParamValue("nuxeo.http.proxy.ntlm.host"),
677                                    collector.getConfigurationParamValue("nuxeo.http.proxy.ntml.domain"));
678                        }
679                    }
680                }
681            }
682        }
683
684        if (ctx.hasErrors()) {
685            currentPage.dispatchToJSP(req, resp);
686        } else {
687            currentPage.next().dispatchToJSP(req, resp, true);
688        }
689    }
690
691    public void handleResetGET(Page currentPage, HttpServletRequest req, HttpServletResponse resp) throws IOException {
692        // reset
693        Context.reset();
694        SimpleNavigationHandler.reset();
695        PackageDownloader.reset();
696
697        // return to first page
698        String target = "/" + req.getContextPath() + "/"
699                + SimpleNavigationHandler.instance().getDefaultPage().getAction();
700        if (target.startsWith("//")) {
701            target = target.substring(1);
702        }
703        resp.sendRedirect(target);
704    }
705
706    public void handlePackageOptionsResourceGET(Page currentPage, HttpServletRequest req, HttpServletResponse resp)
707            throws IOException {
708        DownloadablePackageOptions options = PackageDownloader.instance().getPackageOptions();
709        resp.setContentType("text/json");
710        resp.getWriter().write(options.asJson());
711    }
712
713    public void handlePackagesSelectionGET(Page currentPage, HttpServletRequest req, HttpServletResponse resp)
714            throws ServletException, IOException {
715        handleDefaultGET(currentPage, req, resp);
716    }
717
718    public void handlePackagesSelectionPOST(Page currentPage, HttpServletRequest req, HttpServletResponse resp)
719            throws ServletException, IOException {
720        List<String> options = new ArrayList<>();
721        Enumeration<String> params = req.getParameterNames();
722        while (params.hasMoreElements()) {
723            String p = params.nextElement();
724            if ("on".equals(req.getParameter(p))) {
725                options.add(p);
726            }
727        }
728
729        PackageDownloader.instance().selectOptions(options);
730        currentPage.next().dispatchToJSP(req, resp, true);
731    }
732
733    public void handlePackagesDownloadGET(Page currentPage, HttpServletRequest req, HttpServletResponse resp)
734            throws ServletException, IOException {
735        if ("true".equals(req.getParameter("startDownload"))) {
736            PackageDownloader.instance().startDownload();
737        } else if (req.getParameter("reStartDownload") != null) {
738            PackageDownloader.instance().reStartDownload(req.getParameter("reStartDownload"));
739        }
740        currentPage.dispatchToJSP(req, resp);
741    }
742
743    public void handlePackagesDownloadPOST(Page currentPage, HttpServletRequest req, HttpServletResponse resp)
744            throws ServletException, IOException {
745        ParamCollector collector = Context.instance(req).getCollector();
746
747        String installationFilePath = new File(collector.getConfigurationParam(NUXEO_DATA_DIR),
748                INSTALL_AFTER_RESTART).getAbsolutePath();
749
750        PackageDownloader.instance().scheduleDownloadedPackagesForInstallation(installationFilePath);
751        PackageDownloader.reset();
752        currentPage.next().dispatchToJSP(req, resp, true);
753    }
754
755}