001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.plugininstaller.action;
016    
017    import com.liferay.portal.deploy.DeployUtil;
018    import com.liferay.portal.events.GlobalStartupAction;
019    import com.liferay.portal.kernel.deploy.auto.AutoDeployDir;
020    import com.liferay.portal.kernel.deploy.auto.AutoDeployListener;
021    import com.liferay.portal.kernel.deploy.auto.AutoDeployUtil;
022    import com.liferay.portal.kernel.log.Log;
023    import com.liferay.portal.kernel.log.LogFactoryUtil;
024    import com.liferay.portal.kernel.servlet.SessionErrors;
025    import com.liferay.portal.kernel.servlet.SessionMessages;
026    import com.liferay.portal.kernel.upload.UploadException;
027    import com.liferay.portal.kernel.upload.UploadPortletRequest;
028    import com.liferay.portal.kernel.util.ArrayUtil;
029    import com.liferay.portal.kernel.util.CharPool;
030    import com.liferay.portal.kernel.util.Constants;
031    import com.liferay.portal.kernel.util.FileUtil;
032    import com.liferay.portal.kernel.util.GetterUtil;
033    import com.liferay.portal.kernel.util.HttpUtil;
034    import com.liferay.portal.kernel.util.ParamUtil;
035    import com.liferay.portal.kernel.util.PropsKeys;
036    import com.liferay.portal.kernel.util.ServerDetector;
037    import com.liferay.portal.kernel.util.StringBundler;
038    import com.liferay.portal.kernel.util.StringPool;
039    import com.liferay.portal.kernel.util.StringUtil;
040    import com.liferay.portal.kernel.util.Validator;
041    import com.liferay.portal.plugin.PluginPackageUtil;
042    import com.liferay.portal.plugin.RepositoryReport;
043    import com.liferay.portal.security.auth.PrincipalException;
044    import com.liferay.portal.security.lang.DoPrivilegedBean;
045    import com.liferay.portal.security.permission.PermissionChecker;
046    import com.liferay.portal.struts.PortletAction;
047    import com.liferay.portal.theme.ThemeDisplay;
048    import com.liferay.portal.tools.deploy.BaseDeployer;
049    import com.liferay.portal.upload.ProgressInputStream;
050    import com.liferay.portal.util.HttpImpl;
051    import com.liferay.portal.util.PortalUtil;
052    import com.liferay.portal.util.PrefsPropsUtil;
053    import com.liferay.portal.util.PropsUtil;
054    import com.liferay.portal.util.PropsValues;
055    import com.liferay.portal.util.WebKeys;
056    
057    import java.io.File;
058    import java.io.FileOutputStream;
059    import java.io.IOException;
060    
061    import java.net.MalformedURLException;
062    import java.net.URL;
063    
064    import java.util.List;
065    
066    import javax.portlet.ActionRequest;
067    import javax.portlet.ActionResponse;
068    import javax.portlet.PortletConfig;
069    import javax.portlet.PortletPreferences;
070    
071    import javax.servlet.http.HttpServletResponse;
072    
073    import org.apache.commons.httpclient.HostConfiguration;
074    import org.apache.commons.httpclient.HttpClient;
075    import org.apache.commons.httpclient.methods.GetMethod;
076    import org.apache.struts.action.ActionForm;
077    import org.apache.struts.action.ActionMapping;
078    
079    /**
080     * @author Jorge Ferrer
081     * @author Brian Wing Shun Chan
082     * @author Minhchau Dang
083     */
084    public class InstallPluginAction extends PortletAction {
085    
086            @Override
087            public void processAction(
088                            ActionMapping actionMapping, ActionForm actionForm,
089                            PortletConfig portletConfig, ActionRequest actionRequest,
090                            ActionResponse actionResponse)
091                    throws Exception {
092    
093                    ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(
094                            WebKeys.THEME_DISPLAY);
095    
096                    PermissionChecker permissionChecker =
097                            themeDisplay.getPermissionChecker();
098    
099                    if (!permissionChecker.isOmniadmin()) {
100                            SessionErrors.add(
101                                    actionRequest, PrincipalException.class.getName());
102    
103                            setForward(actionRequest, "portlet.plugin_installer.error");
104    
105                            return;
106                    }
107    
108                    String cmd = ParamUtil.getString(actionRequest, Constants.CMD);
109    
110                    if (cmd.equals("deployConfiguration")) {
111                            deployConfiguration(actionRequest);
112                    }
113                    else if (cmd.equals("ignorePackages")) {
114                            ignorePackages(actionRequest);
115                    }
116                    else if (cmd.equals("localDeploy")) {
117                            localDeploy(actionRequest);
118                    }
119                    else if (cmd.equals("reloadRepositories")) {
120                            reloadRepositories(actionRequest);
121                    }
122                    else if (cmd.equals("remoteDeploy")) {
123                            remoteDeploy(actionRequest);
124                    }
125                    else if (cmd.equals("unignorePackages")) {
126                            unignorePackages(actionRequest);
127                    }
128                    else if (cmd.equals("uninstall")) {
129                            uninstall(actionRequest);
130                    }
131    
132                    sendRedirect(actionRequest, actionResponse);
133            }
134    
135            protected void deployConfiguration(ActionRequest actionRequest)
136                    throws Exception {
137    
138                    boolean enabled = ParamUtil.getBoolean(actionRequest, "enabled");
139                    String deployDir = ParamUtil.getString(actionRequest, "deployDir");
140                    String destDir = ParamUtil.getString(actionRequest, "destDir");
141                    long interval = ParamUtil.getLong(actionRequest, "interval");
142                    int blacklistThreshold = ParamUtil.getInteger(
143                            actionRequest, "blacklistThreshold");
144                    boolean unpackWar = ParamUtil.getBoolean(actionRequest, "unpackWar");
145                    boolean customPortletXml = ParamUtil.getBoolean(
146                            actionRequest, "customPortletXml");
147                    String jbossPrefix = ParamUtil.getString(actionRequest, "jbossPrefix");
148                    String tomcatConfDir = ParamUtil.getString(
149                            actionRequest, "tomcatConfDir");
150                    String tomcatLibDir = ParamUtil.getString(
151                            actionRequest, "tomcatLibDir");
152                    String pluginRepositoriesTrusted = ParamUtil.getString(
153                            actionRequest, "pluginRepositoriesTrusted");
154                    String pluginRepositoriesUntrusted = ParamUtil.getString(
155                            actionRequest, "pluginRepositoriesUntrusted");
156                    boolean pluginNotificationsEnabled = ParamUtil.getBoolean(
157                            actionRequest, "pluginNotificationsEnabled");
158                    String pluginPackagesIgnored = ParamUtil.getString(
159                            actionRequest, "pluginPackagesIgnored");
160    
161                    PortletPreferences preferences = PrefsPropsUtil.getPreferences();
162    
163                    preferences.setValue(
164                            PropsKeys.AUTO_DEPLOY_ENABLED, String.valueOf(enabled));
165                    preferences.setValue(PropsKeys.AUTO_DEPLOY_DEPLOY_DIR, deployDir);
166                    preferences.setValue(PropsKeys.AUTO_DEPLOY_DEST_DIR, destDir);
167                    preferences.setValue(
168                            PropsKeys.AUTO_DEPLOY_INTERVAL, String.valueOf(interval));
169                    preferences.setValue(
170                            PropsKeys.AUTO_DEPLOY_BLACKLIST_THRESHOLD,
171                            String.valueOf(blacklistThreshold));
172                    preferences.setValue(
173                            PropsKeys.AUTO_DEPLOY_UNPACK_WAR, String.valueOf(unpackWar));
174                    preferences.setValue(
175                            PropsKeys.AUTO_DEPLOY_CUSTOM_PORTLET_XML,
176                            String.valueOf(customPortletXml));
177                    preferences.setValue(PropsKeys.AUTO_DEPLOY_JBOSS_PREFIX, jbossPrefix);
178                    preferences.setValue(
179                            PropsKeys.AUTO_DEPLOY_TOMCAT_CONF_DIR, tomcatConfDir);
180                    preferences.setValue(
181                            PropsKeys.AUTO_DEPLOY_TOMCAT_LIB_DIR, tomcatLibDir);
182                    preferences.setValue(
183                            PropsKeys.PLUGIN_REPOSITORIES_TRUSTED, pluginRepositoriesTrusted);
184                    preferences.setValue(
185                            PropsKeys.PLUGIN_REPOSITORIES_UNTRUSTED,
186                            pluginRepositoriesUntrusted);
187                    preferences.setValue(
188                            PropsKeys.PLUGIN_NOTIFICATIONS_ENABLED,
189                            String.valueOf(pluginNotificationsEnabled));
190                    preferences.setValue(
191                            PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
192                            pluginPackagesIgnored);
193    
194                    preferences.store();
195    
196                    reloadRepositories(actionRequest);
197    
198                    if (_log.isInfoEnabled()) {
199                            _log.info("Unregistering auto deploy directories");
200                    }
201    
202                    AutoDeployUtil.unregisterDir("defaultAutoDeployDir");
203    
204                    if (enabled) {
205                            if (_log.isInfoEnabled()) {
206                                    _log.info("Registering auto deploy directories");
207                            }
208    
209                            List<AutoDeployListener> autoDeployListeners =
210                                    GlobalStartupAction.getAutoDeployListeners(true);
211    
212                            AutoDeployDir autoDeployDir = new AutoDeployDir(
213                                    "defaultAutoDeployDir", new File(deployDir), new File(destDir),
214                                    interval, blacklistThreshold, autoDeployListeners);
215    
216                            AutoDeployUtil.registerDir(autoDeployDir);
217                    }
218                    else {
219                            if (_log.isInfoEnabled()) {
220                                    _log.info("Not registering auto deploy directories");
221                            }
222                    }
223            }
224    
225            protected String[] getSourceForgeMirrors() {
226                    return PropsUtil.getArray(PropsKeys.SOURCE_FORGE_MIRRORS);
227            }
228    
229            protected void ignorePackages(ActionRequest actionRequest)
230                    throws Exception {
231    
232                    String pluginPackagesIgnored = ParamUtil.getString(
233                            actionRequest, "pluginPackagesIgnored");
234    
235                    String oldPluginPackagesIgnored = PrefsPropsUtil.getString(
236                            PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED);
237    
238                    PortletPreferences preferences = PrefsPropsUtil.getPreferences();
239    
240                    if (Validator.isNotNull(oldPluginPackagesIgnored)) {
241                            preferences.setValue(
242                                    PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
243                                    oldPluginPackagesIgnored.concat(StringPool.NEW_LINE).concat(
244                                            pluginPackagesIgnored));
245                    }
246                    else {
247                            preferences.setValue(
248                                    PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
249                                    pluginPackagesIgnored);
250                    }
251    
252                    preferences.store();
253    
254                    PluginPackageUtil.refreshUpdatesAvailableCache();
255            }
256    
257            protected void localDeploy(ActionRequest actionRequest) throws Exception {
258                    UploadPortletRequest uploadPortletRequest =
259                            PortalUtil.getUploadPortletRequest(actionRequest);
260    
261                    String fileName = null;
262    
263                    String deploymentContext = ParamUtil.getString(
264                            actionRequest, "deploymentContext");
265    
266                    if (Validator.isNotNull(deploymentContext)) {
267                            fileName =
268                                    BaseDeployer.DEPLOY_TO_PREFIX + deploymentContext + ".war";
269                    }
270                    else {
271                            fileName = GetterUtil.getString(
272                                    uploadPortletRequest.getFileName("file"));
273    
274                            int pos = fileName.lastIndexOf(CharPool.PERIOD);
275    
276                            if (pos != -1) {
277                                    deploymentContext = fileName.substring(0, pos);
278                            }
279                    }
280    
281                    File file = uploadPortletRequest.getFile("file");
282    
283                    byte[] bytes = FileUtil.getBytes(file);
284    
285                    if ((bytes == null) || (bytes.length == 0)) {
286                            SessionErrors.add(actionRequest, UploadException.class.getName());
287    
288                            return;
289                    }
290    
291                    try {
292                            PluginPackageUtil.registerPluginPackageInstallation(
293                                    deploymentContext);
294    
295                            String source = file.toString();
296    
297                            String deployDir = PrefsPropsUtil.getString(
298                                    PropsKeys.AUTO_DEPLOY_DEPLOY_DIR,
299                                    PropsValues.AUTO_DEPLOY_DEPLOY_DIR);
300    
301                            String destination = deployDir + StringPool.SLASH + fileName;
302    
303                            FileUtil.copyFile(source, destination);
304    
305                            SessionMessages.add(actionRequest, "pluginUploaded");
306                    }
307                    finally {
308                            PluginPackageUtil.endPluginPackageInstallation(deploymentContext);
309                    }
310            }
311    
312            protected void reloadRepositories(ActionRequest actionRequest)
313                    throws Exception {
314    
315                    RepositoryReport repositoryReport =
316                            PluginPackageUtil.reloadRepositories();
317    
318                    SessionMessages.add(
319                            actionRequest, WebKeys.PLUGIN_REPOSITORY_REPORT, repositoryReport);
320            }
321    
322            protected void remoteDeploy(ActionRequest actionRequest) throws Exception {
323                    try {
324                            String url = ParamUtil.getString(actionRequest, "url");
325    
326                            URL urlObj = new URL(url);
327    
328                            String host = urlObj.getHost();
329    
330                            if (host.endsWith(".sf.net") || host.endsWith(".sourceforge.net")) {
331                                    remoteDeploySourceForge(urlObj.getPath(), actionRequest);
332                            }
333                            else {
334                                    remoteDeploy(url, urlObj, actionRequest, true);
335                            }
336                    }
337                    catch (MalformedURLException murle) {
338                            SessionErrors.add(actionRequest, "invalidUrl", murle);
339                    }
340            }
341    
342            protected int remoteDeploy(
343                            String url, URL urlObj, ActionRequest actionRequest,
344                            boolean failOnError)
345                    throws Exception {
346    
347                    int responseCode = HttpServletResponse.SC_OK;
348    
349                    GetMethod getMethod = null;
350    
351                    String deploymentContext = ParamUtil.getString(
352                            actionRequest, "deploymentContext");
353    
354                    try {
355                            HttpImpl httpImpl = null;
356    
357                            Object httpObject = HttpUtil.getHttp();
358    
359                            if (httpObject instanceof DoPrivilegedBean) {
360                                    DoPrivilegedBean doPrivilegedBean =
361                                            (DoPrivilegedBean)httpObject;
362    
363                                    httpImpl = (HttpImpl)doPrivilegedBean.getActualBean();
364                            }
365                            else {
366                                    httpImpl = (HttpImpl)httpObject;
367                            }
368    
369                            HostConfiguration hostConfiguration = httpImpl.getHostConfiguration(
370                                    url);
371    
372                            HttpClient httpClient = httpImpl.getClient(hostConfiguration);
373    
374                            getMethod = new GetMethod(url);
375    
376                            String fileName = null;
377    
378                            if (Validator.isNotNull(deploymentContext)) {
379                                    fileName =
380                                            BaseDeployer.DEPLOY_TO_PREFIX + deploymentContext + ".war";
381                            }
382                            else {
383                                    fileName = url.substring(url.lastIndexOf(CharPool.SLASH) + 1);
384    
385                                    int pos = fileName.lastIndexOf(CharPool.PERIOD);
386    
387                                    if (pos != -1) {
388                                            deploymentContext = fileName.substring(0, pos);
389                                    }
390                            }
391    
392                            PluginPackageUtil.registerPluginPackageInstallation(
393                                    deploymentContext);
394    
395                            responseCode = httpClient.executeMethod(
396                                    hostConfiguration, getMethod);
397    
398                            if (responseCode != HttpServletResponse.SC_OK) {
399                                    if (failOnError) {
400                                            SessionErrors.add(
401                                                    actionRequest, "errorConnectingToUrl",
402                                                    new Object[] {String.valueOf(responseCode)});
403                                    }
404    
405                                    return responseCode;
406                            }
407    
408                            long contentLength = getMethod.getResponseContentLength();
409    
410                            String progressId = ParamUtil.getString(
411                                    actionRequest, Constants.PROGRESS_ID);
412    
413                            ProgressInputStream pis = new ProgressInputStream(
414                                    actionRequest, getMethod.getResponseBodyAsStream(),
415                                    contentLength, progressId);
416    
417                            String deployDir = PrefsPropsUtil.getString(
418                                    PropsKeys.AUTO_DEPLOY_DEPLOY_DIR,
419                                    PropsValues.AUTO_DEPLOY_DEPLOY_DIR);
420    
421                            String tmpFilePath =
422                                    deployDir + StringPool.SLASH + _DOWNLOAD_DIR +
423                                            StringPool.SLASH + fileName;
424    
425                            File tmpFile = new File(tmpFilePath);
426    
427                            if (!tmpFile.getParentFile().exists()) {
428                                    tmpFile.getParentFile().mkdirs();
429                            }
430    
431                            FileOutputStream fos = new FileOutputStream(tmpFile);
432    
433                            try {
434                                    pis.readAll(fos);
435    
436                                    if (_log.isInfoEnabled()) {
437                                            _log.info(
438                                                    "Downloaded plugin from " + urlObj + " has " +
439                                                            pis.getTotalRead() + " bytes");
440                                    }
441                            }
442                            finally {
443                                    pis.clearProgress();
444                            }
445    
446                            getMethod.releaseConnection();
447    
448                            if (pis.getTotalRead() > 0) {
449                                    String destination = deployDir + StringPool.SLASH + fileName;
450    
451                                    File destinationFile = new File(destination);
452    
453                                    boolean moved = FileUtil.move(tmpFile, destinationFile);
454    
455                                    if (!moved) {
456                                            FileUtil.copyFile(tmpFile, destinationFile);
457                                            FileUtil.delete(tmpFile);
458                                    }
459    
460                                    SessionMessages.add(actionRequest, "pluginDownloaded");
461                            }
462                            else {
463                                    if (failOnError) {
464                                            SessionErrors.add(
465                                                    actionRequest, UploadException.class.getName());
466                                    }
467    
468                                    responseCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
469                            }
470                    }
471                    catch (MalformedURLException murle) {
472                            SessionErrors.add(actionRequest, "invalidUrl", murle);
473                    }
474                    catch (IOException ioe) {
475                            SessionErrors.add(actionRequest, "errorConnectingToUrl", ioe);
476                    }
477                    finally {
478                            if (getMethod != null) {
479                                    getMethod.releaseConnection();
480                            }
481    
482                            PluginPackageUtil.endPluginPackageInstallation(deploymentContext);
483                    }
484    
485                    return responseCode;
486            }
487    
488            protected void remoteDeploySourceForge(
489                            String path, ActionRequest actionRequest)
490                    throws Exception {
491    
492                    String[] sourceForgeMirrors = getSourceForgeMirrors();
493    
494                    for (int i = 0; i < sourceForgeMirrors.length; i++) {
495                            try {
496                                    String url = sourceForgeMirrors[i] + path;
497    
498                                    if (_log.isDebugEnabled()) {
499                                            _log.debug("Downloading from SourceForge mirror " + url);
500                                    }
501    
502                                    URL urlObj = new URL(url);
503    
504                                    boolean failOnError = false;
505    
506                                    if ((i + 1) == sourceForgeMirrors.length) {
507                                            failOnError = true;
508                                    }
509    
510                                    int responseCode = remoteDeploy(
511                                            url, urlObj, actionRequest, failOnError);
512    
513                                    if (responseCode == HttpServletResponse.SC_OK) {
514                                            return;
515                                    }
516                            }
517                            catch (MalformedURLException murle) {
518                                    SessionErrors.add(actionRequest, "invalidUrl", murle);
519                            }
520                    }
521            }
522    
523            protected void unignorePackages(ActionRequest actionRequest)
524                    throws Exception {
525    
526                    String[] pluginPackagesUnignored = StringUtil.splitLines(
527                            ParamUtil.getString(actionRequest, "pluginPackagesUnignored"));
528    
529                    String[] pluginPackagesIgnored = PrefsPropsUtil.getStringArray(
530                            PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
531                            StringPool.NEW_LINE,
532                            PropsValues.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED);
533    
534                    StringBundler sb = new StringBundler();
535    
536                    for (int i = 0; i < pluginPackagesIgnored.length; i++) {
537                            String packageId = pluginPackagesIgnored[i];
538    
539                            if (!ArrayUtil.contains(pluginPackagesUnignored, packageId)) {
540                                    sb.append(packageId);
541                                    sb.append(StringPool.NEW_LINE);
542                            }
543                    }
544    
545                    PortletPreferences preferences = PrefsPropsUtil.getPreferences();
546    
547                    preferences.setValue(
548                            PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED, sb.toString());
549    
550                    preferences.store();
551    
552                    PluginPackageUtil.refreshUpdatesAvailableCache();
553            }
554    
555            protected void uninstall(ActionRequest actionRequest) throws Exception {
556                    String appServerType = ServerDetector.getServerId();
557    
558                    String deploymentContext = ParamUtil.getString(
559                            actionRequest, "deploymentContext");
560    
561                    File deployDir = new File(
562                            DeployUtil.getAutoDeployDestDir(), deploymentContext);
563    
564                    DeployUtil.undeploy(appServerType, deployDir);
565    
566                    SessionMessages.add(actionRequest, "triggeredPortletUndeploy");
567            }
568    
569            private static final String _DOWNLOAD_DIR = "download";
570    
571            private static Log _log = LogFactoryUtil.getLog(InstallPluginAction.class);
572    
573    }