1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portlet.plugininstaller.action;
16  
17  import com.liferay.portal.deploy.DeployUtil;
18  import com.liferay.portal.events.GlobalStartupAction;
19  import com.liferay.portal.kernel.deploy.auto.AutoDeployDir;
20  import com.liferay.portal.kernel.deploy.auto.AutoDeployListener;
21  import com.liferay.portal.kernel.deploy.auto.AutoDeployUtil;
22  import com.liferay.portal.kernel.log.Log;
23  import com.liferay.portal.kernel.log.LogFactoryUtil;
24  import com.liferay.portal.kernel.servlet.SessionErrors;
25  import com.liferay.portal.kernel.servlet.SessionMessages;
26  import com.liferay.portal.kernel.upload.UploadPortletRequest;
27  import com.liferay.portal.kernel.util.ArrayUtil;
28  import com.liferay.portal.kernel.util.Constants;
29  import com.liferay.portal.kernel.util.FileUtil;
30  import com.liferay.portal.kernel.util.GetterUtil;
31  import com.liferay.portal.kernel.util.HttpUtil;
32  import com.liferay.portal.kernel.util.ParamUtil;
33  import com.liferay.portal.kernel.util.PropsKeys;
34  import com.liferay.portal.kernel.util.ServerDetector;
35  import com.liferay.portal.kernel.util.StringBundler;
36  import com.liferay.portal.kernel.util.StringPool;
37  import com.liferay.portal.kernel.util.StringUtil;
38  import com.liferay.portal.kernel.util.Validator;
39  import com.liferay.portal.plugin.PluginPackageUtil;
40  import com.liferay.portal.plugin.RepositoryReport;
41  import com.liferay.portal.security.auth.PrincipalException;
42  import com.liferay.portal.security.permission.PermissionChecker;
43  import com.liferay.portal.struts.PortletAction;
44  import com.liferay.portal.theme.ThemeDisplay;
45  import com.liferay.portal.tools.deploy.BaseDeployer;
46  import com.liferay.portal.upload.ProgressInputStream;
47  import com.liferay.portal.util.HttpImpl;
48  import com.liferay.portal.util.PortalUtil;
49  import com.liferay.portal.util.PrefsPropsUtil;
50  import com.liferay.portal.util.PropsUtil;
51  import com.liferay.portal.util.PropsValues;
52  import com.liferay.portal.util.WebKeys;
53  import com.liferay.util.servlet.UploadException;
54  
55  import java.io.File;
56  import java.io.FileOutputStream;
57  import java.io.IOException;
58  
59  import java.net.MalformedURLException;
60  import java.net.URL;
61  
62  import java.util.List;
63  
64  import javax.portlet.ActionRequest;
65  import javax.portlet.ActionResponse;
66  import javax.portlet.PortletConfig;
67  import javax.portlet.PortletPreferences;
68  
69  import javax.servlet.http.HttpServletResponse;
70  
71  import org.apache.commons.httpclient.HostConfiguration;
72  import org.apache.commons.httpclient.HttpClient;
73  import org.apache.commons.httpclient.methods.GetMethod;
74  import org.apache.struts.action.ActionForm;
75  import org.apache.struts.action.ActionMapping;
76  
77  /**
78   * <a href="InstallPluginAction.java.html"><b><i>View Source</i></b></a>
79   *
80   * @author Jorge Ferrer
81   * @author Brian Wing Shun Chan
82   * @author Minhchau Dang
83   */
84  public class InstallPluginAction extends PortletAction {
85  
86      public void processAction(
87              ActionMapping mapping, ActionForm form, PortletConfig portletConfig,
88              ActionRequest actionRequest, ActionResponse actionResponse)
89          throws Exception {
90  
91          ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(
92              WebKeys.THEME_DISPLAY);
93  
94          PermissionChecker permissionChecker =
95              themeDisplay.getPermissionChecker();
96  
97          if (!permissionChecker.isOmniadmin()) {
98              SessionErrors.add(
99                  actionRequest, PrincipalException.class.getName());
100 
101             setForward(actionRequest, "portlet.plugin_installer.error");
102 
103             return;
104         }
105 
106         String cmd = ParamUtil.getString(actionRequest, Constants.CMD);
107 
108         if (cmd.equals("deployConfiguration")) {
109             deployConfiguration(actionRequest);
110         }
111         else if (cmd.equals("ignorePackages")) {
112             ignorePackages(actionRequest);
113         }
114         else if (cmd.equals("localDeploy")) {
115             localDeploy(actionRequest);
116         }
117         else if (cmd.equals("reloadRepositories")) {
118             reloadRepositories(actionRequest);
119         }
120         else if (cmd.equals("remoteDeploy")) {
121             remoteDeploy(actionRequest);
122         }
123         else if (cmd.equals("unignorePackages")) {
124             unignorePackages(actionRequest);
125         }
126         else if (cmd.equals("uninstall")) {
127             uninstall(actionRequest);
128         }
129 
130         sendRedirect(actionRequest, actionResponse);
131     }
132 
133     protected void deployConfiguration(ActionRequest actionRequest)
134         throws Exception {
135 
136         boolean enabled = ParamUtil.getBoolean(actionRequest, "enabled");
137         String deployDir = ParamUtil.getString(actionRequest, "deployDir");
138         String destDir = ParamUtil.getString(actionRequest, "destDir");
139         long interval = ParamUtil.getLong(actionRequest, "interval");
140         int blacklistThreshold = ParamUtil.getInteger(
141             actionRequest, "blacklistThreshold");
142         boolean unpackWar = ParamUtil.getBoolean(actionRequest, "unpackWar");
143         boolean customPortletXml = ParamUtil.getBoolean(
144             actionRequest, "customPortletXml");
145         String jbossPrefix = ParamUtil.getString(actionRequest, "jbossPrefix");
146         String tomcatConfDir = ParamUtil.getString(
147             actionRequest, "tomcatConfDir");
148         String tomcatLibDir = ParamUtil.getString(
149             actionRequest, "tomcatLibDir");
150         String pluginRepositoriesTrusted = ParamUtil.getString(
151             actionRequest, "pluginRepositoriesTrusted");
152         String pluginRepositoriesUntrusted = ParamUtil.getString(
153             actionRequest, "pluginRepositoriesUntrusted");
154         boolean pluginNotificationsEnabled = ParamUtil.getBoolean(
155             actionRequest, "pluginNotificationsEnabled");
156         String pluginPackagesIgnored = ParamUtil.getString(
157             actionRequest, "pluginPackagesIgnored");
158 
159         PortletPreferences preferences = PrefsPropsUtil.getPreferences();
160 
161         preferences.setValue(
162             PropsKeys.AUTO_DEPLOY_ENABLED, String.valueOf(enabled));
163         preferences.setValue(PropsKeys.AUTO_DEPLOY_DEPLOY_DIR, deployDir);
164         preferences.setValue(PropsKeys.AUTO_DEPLOY_DEST_DIR, destDir);
165         preferences.setValue(
166             PropsKeys.AUTO_DEPLOY_INTERVAL, String.valueOf(interval));
167         preferences.setValue(
168             PropsKeys.AUTO_DEPLOY_BLACKLIST_THRESHOLD,
169             String.valueOf(blacklistThreshold));
170         preferences.setValue(
171             PropsKeys.AUTO_DEPLOY_UNPACK_WAR, String.valueOf(unpackWar));
172         preferences.setValue(
173             PropsKeys.AUTO_DEPLOY_CUSTOM_PORTLET_XML,
174             String.valueOf(customPortletXml));
175         preferences.setValue(PropsKeys.AUTO_DEPLOY_JBOSS_PREFIX, jbossPrefix);
176         preferences.setValue(
177             PropsKeys.AUTO_DEPLOY_TOMCAT_CONF_DIR, tomcatConfDir);
178         preferences.setValue(
179             PropsKeys.AUTO_DEPLOY_TOMCAT_LIB_DIR, tomcatLibDir);
180         preferences.setValue(
181             PropsKeys.PLUGIN_REPOSITORIES_TRUSTED, pluginRepositoriesTrusted);
182         preferences.setValue(
183             PropsKeys.PLUGIN_REPOSITORIES_UNTRUSTED,
184             pluginRepositoriesUntrusted);
185         preferences.setValue(
186             PropsKeys.PLUGIN_NOTIFICATIONS_ENABLED,
187             String.valueOf(pluginNotificationsEnabled));
188         preferences.setValue(
189             PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
190             pluginPackagesIgnored);
191 
192         preferences.store();
193 
194         reloadRepositories(actionRequest);
195 
196         if (_log.isInfoEnabled()) {
197             _log.info("Unregistering auto deploy directories");
198         }
199 
200         AutoDeployUtil.unregisterDir("defaultAutoDeployDir");
201 
202         if (enabled) {
203             if (_log.isInfoEnabled()) {
204                 _log.info("Registering auto deploy directories");
205             }
206 
207             List<AutoDeployListener> autoDeployListeners =
208                 GlobalStartupAction.getAutoDeployListeners();
209 
210             AutoDeployDir autoDeployDir = new AutoDeployDir(
211                 "defaultAutoDeployDir", new File(deployDir), new File(destDir),
212                 interval, blacklistThreshold, autoDeployListeners);
213 
214             AutoDeployUtil.registerDir(autoDeployDir);
215         }
216         else {
217             if (_log.isInfoEnabled()) {
218                 _log.info("Not registering auto deploy directories");
219             }
220         }
221     }
222 
223     protected String[] getSourceForgeMirrors() {
224         return PropsUtil.getArray(PropsKeys.SOURCE_FORGE_MIRRORS);
225     }
226 
227     protected void ignorePackages(ActionRequest actionRequest)
228         throws Exception {
229 
230         String pluginPackagesIgnored = ParamUtil.getString(
231             actionRequest, "pluginPackagesIgnored");
232 
233         String oldPluginPackagesIgnored= PrefsPropsUtil.getString(
234             PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED);
235 
236         PortletPreferences preferences = PrefsPropsUtil.getPreferences();
237 
238         preferences.setValue(
239             PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
240             oldPluginPackagesIgnored.concat(StringPool.NEW_LINE).concat(
241                 pluginPackagesIgnored));
242 
243         preferences.store();
244 
245         PluginPackageUtil.refreshUpdatesAvailableCache();
246     }
247 
248     protected void localDeploy(ActionRequest actionRequest) throws Exception {
249         UploadPortletRequest uploadRequest = PortalUtil.getUploadPortletRequest(
250             actionRequest);
251 
252         String fileName = null;
253 
254         String deploymentContext = ParamUtil.getString(
255             actionRequest, "deploymentContext");
256 
257         if (Validator.isNotNull(deploymentContext)) {
258             fileName =
259                 BaseDeployer.DEPLOY_TO_PREFIX + deploymentContext + ".war";
260         }
261         else {
262             fileName = GetterUtil.getString(uploadRequest.getFileName("file"));
263 
264             int pos = fileName.lastIndexOf(StringPool.PERIOD);
265 
266             if (pos != -1) {
267                 deploymentContext = fileName.substring(0, pos);
268             }
269         }
270 
271         File file = uploadRequest.getFile("file");
272 
273         byte[] bytes = FileUtil.getBytes(file);
274 
275         if ((bytes == null) || (bytes.length == 0)) {
276             SessionErrors.add(actionRequest, UploadException.class.getName());
277 
278             return;
279         }
280 
281         try {
282             PluginPackageUtil.registerPluginPackageInstallation(
283                 deploymentContext);
284 
285             String source = file.toString();
286 
287             String deployDir = PrefsPropsUtil.getString(
288                 PropsKeys.AUTO_DEPLOY_DEPLOY_DIR,
289                 PropsValues.AUTO_DEPLOY_DEPLOY_DIR);
290 
291             String destination = deployDir + StringPool.SLASH + fileName;
292 
293             FileUtil.copyFile(source, destination);
294 
295             SessionMessages.add(actionRequest, "pluginUploaded");
296         }
297         finally {
298             PluginPackageUtil.endPluginPackageInstallation(deploymentContext);
299         }
300     }
301 
302     protected void reloadRepositories(ActionRequest actionRequest)
303         throws Exception {
304 
305         RepositoryReport repositoryReport =
306             PluginPackageUtil.reloadRepositories();
307 
308         SessionMessages.add(
309             actionRequest, WebKeys.PLUGIN_REPOSITORY_REPORT, repositoryReport);
310     }
311 
312     protected void remoteDeploy(ActionRequest actionRequest) throws Exception {
313         try {
314             String url = ParamUtil.getString(actionRequest, "url");
315 
316             URL urlObj = new URL(url);
317 
318             String host = urlObj.getHost();
319 
320             if (host.endsWith(".sf.net") || host.endsWith(".sourceforge.net")) {
321                 remoteDeploySourceForge(urlObj.getPath(), actionRequest);
322             }
323             else {
324                 remoteDeploy(url, urlObj, actionRequest, true);
325             }
326         }
327         catch (MalformedURLException murle) {
328             SessionErrors.add(actionRequest, "invalidUrl", murle);
329         }
330     }
331 
332     protected int remoteDeploy(
333             String url, URL urlObj, ActionRequest actionRequest,
334             boolean failOnError)
335         throws Exception {
336 
337         int responseCode = HttpServletResponse.SC_OK;
338 
339         GetMethod getMethod = null;
340 
341         String deploymentContext = ParamUtil.getString(
342             actionRequest, "deploymentContext");
343 
344         try {
345             HttpImpl httpImpl = (HttpImpl)HttpUtil.getHttp();
346 
347             HostConfiguration hostConfig = httpImpl.getHostConfig(url);
348 
349             HttpClient client = httpImpl.getClient(hostConfig);
350 
351             getMethod = new GetMethod(url);
352 
353             String fileName = null;
354 
355             if (Validator.isNotNull(deploymentContext)) {
356                 fileName =
357                     BaseDeployer.DEPLOY_TO_PREFIX + deploymentContext + ".war";
358             }
359             else {
360                 fileName = url.substring(url.lastIndexOf(StringPool.SLASH) + 1);
361 
362                 int pos = fileName.lastIndexOf(StringPool.PERIOD);
363 
364                 if (pos != -1) {
365                     deploymentContext = fileName.substring(0, pos);
366                 }
367             }
368 
369             PluginPackageUtil.registerPluginPackageInstallation(
370                 deploymentContext);
371 
372             responseCode = client.executeMethod(hostConfig, getMethod);
373 
374             if (responseCode != HttpServletResponse.SC_OK) {
375                 if (failOnError) {
376                     SessionErrors.add(
377                         actionRequest, "errorConnectingToUrl",
378                         new Object[] {String.valueOf(responseCode)});
379                 }
380 
381                 return responseCode;
382             }
383 
384             long contentLength = getMethod.getResponseContentLength();
385 
386             String progressId = ParamUtil.getString(
387                 actionRequest, Constants.PROGRESS_ID);
388 
389             ProgressInputStream pis = new ProgressInputStream(
390                 actionRequest, getMethod.getResponseBodyAsStream(),
391                 contentLength, progressId);
392 
393             String deployDir = PrefsPropsUtil.getString(
394                 PropsKeys.AUTO_DEPLOY_DEPLOY_DIR,
395                 PropsValues.AUTO_DEPLOY_DEPLOY_DIR);
396 
397             String tmpFilePath =
398                 deployDir + StringPool.SLASH + _DOWNLOAD_DIR +
399                     StringPool.SLASH + fileName;
400 
401             File tmpFile = new File(tmpFilePath);
402 
403             if (!tmpFile.getParentFile().exists()) {
404                 tmpFile.getParentFile().mkdirs();
405             }
406 
407             FileOutputStream fos = new FileOutputStream(tmpFile);
408 
409             try {
410                 pis.readAll(fos);
411 
412                 if (_log.isInfoEnabled()) {
413                     _log.info(
414                         "Downloaded plugin from " + urlObj + " has " +
415                             pis.getTotalRead() + " bytes");
416                 }
417             }
418             finally {
419                 pis.clearProgress();
420             }
421 
422             getMethod.releaseConnection();
423 
424             if (pis.getTotalRead() > 0) {
425                 String destination = deployDir + StringPool.SLASH + fileName;
426 
427                 File destinationFile = new File(destination);
428 
429                 boolean moved = FileUtil.move(tmpFile, destinationFile);
430 
431                 if (!moved) {
432                     FileUtil.copyFile(tmpFile, destinationFile);
433                     FileUtil.delete(tmpFile);
434                 }
435 
436                 SessionMessages.add(actionRequest, "pluginDownloaded");
437             }
438             else {
439                 if (failOnError) {
440                     SessionErrors.add(
441                         actionRequest, UploadException.class.getName());
442                 }
443 
444                 responseCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
445             }
446         }
447         catch (MalformedURLException murle) {
448             SessionErrors.add(actionRequest, "invalidUrl", murle);
449         }
450         catch (IOException ioe) {
451             SessionErrors.add(actionRequest, "errorConnectingToUrl", ioe);
452         }
453         finally {
454             if (getMethod != null) {
455                 getMethod.releaseConnection();
456             }
457 
458             PluginPackageUtil.endPluginPackageInstallation(deploymentContext);
459         }
460 
461         return responseCode;
462     }
463 
464     protected void remoteDeploySourceForge(
465             String path, ActionRequest actionRequest)
466         throws Exception {
467 
468         String[] sourceForgeMirrors = getSourceForgeMirrors();
469 
470         for (int i = 0; i < sourceForgeMirrors.length; i++) {
471             try {
472                 String url = sourceForgeMirrors[i] + path;
473 
474                 if (_log.isDebugEnabled()) {
475                     _log.debug("Downloading from SourceForge mirror " + url);
476                 }
477 
478                 URL urlObj = new URL(url);
479 
480                 boolean failOnError = false;
481 
482                 if ((i + 1) == sourceForgeMirrors.length) {
483                     failOnError = true;
484                 }
485 
486                 int responseCode = remoteDeploy(
487                     url, urlObj, actionRequest, failOnError);
488 
489                 if (responseCode == HttpServletResponse.SC_OK) {
490                     return;
491                 }
492             }
493             catch (MalformedURLException murle) {
494                 SessionErrors.add(actionRequest, "invalidUrl", murle);
495             }
496         }
497     }
498 
499     protected void unignorePackages(ActionRequest actionRequest)
500         throws Exception {
501 
502         String[] pluginPackagesUnignored = StringUtil.split(
503             ParamUtil.getString(actionRequest, "pluginPackagesUnignored"),
504             StringPool.NEW_LINE);
505 
506         String[] pluginPackagesIgnored = PrefsPropsUtil.getStringArray(
507             PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
508             StringPool.NEW_LINE,
509             PropsValues.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED);
510 
511         StringBundler sb = new StringBundler();
512 
513         for (int i = 0; i < pluginPackagesIgnored.length; i++) {
514             String packageId = pluginPackagesIgnored[i];
515 
516             if (!ArrayUtil.contains(pluginPackagesUnignored, packageId)) {
517                 sb.append(packageId);
518                 sb.append(StringPool.NEW_LINE);
519             }
520         }
521 
522         PortletPreferences preferences = PrefsPropsUtil.getPreferences();
523 
524         preferences.setValue(
525             PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED, sb.toString());
526 
527         preferences.store();
528 
529         PluginPackageUtil.refreshUpdatesAvailableCache();
530     }
531 
532     protected void uninstall(ActionRequest actionRequest) throws Exception {
533         String appServerType = ServerDetector.getServerId();
534 
535         String deploymentContext = ParamUtil.getString(
536             actionRequest, "deploymentContext");
537 
538         if (appServerType.startsWith(ServerDetector.JBOSS_ID)) {
539             deploymentContext += ".war";
540         }
541 
542         File deployDir = new File(
543             DeployUtil.getAutoDeployDestDir() + "/" + deploymentContext);
544 
545         DeployUtil.undeploy(appServerType, deployDir);
546     }
547 
548     private static final String _DOWNLOAD_DIR = "download";
549 
550     private static Log _log = LogFactoryUtil.getLog(InstallPluginAction.class);
551 
552 }