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