1   /**
2    * Copyright (c) 2000-2009 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.log.Log;
31  import com.liferay.portal.kernel.log.LogFactoryUtil;
32  import com.liferay.portal.kernel.servlet.SessionErrors;
33  import com.liferay.portal.kernel.servlet.SessionMessages;
34  import com.liferay.portal.kernel.upload.UploadPortletRequest;
35  import com.liferay.portal.kernel.util.ArrayUtil;
36  import com.liferay.portal.kernel.util.Constants;
37  import com.liferay.portal.kernel.util.FileUtil;
38  import com.liferay.portal.kernel.util.GetterUtil;
39  import com.liferay.portal.kernel.util.HttpUtil;
40  import com.liferay.portal.kernel.util.ParamUtil;
41  import com.liferay.portal.kernel.util.ServerDetector;
42  import com.liferay.portal.kernel.util.StringPool;
43  import com.liferay.portal.kernel.util.StringUtil;
44  import com.liferay.portal.kernel.util.Validator;
45  import com.liferay.portal.plugin.PluginPackageUtil;
46  import com.liferay.portal.plugin.RepositoryReport;
47  import com.liferay.portal.security.auth.PrincipalException;
48  import com.liferay.portal.security.permission.PermissionChecker;
49  import com.liferay.portal.struts.PortletAction;
50  import com.liferay.portal.theme.ThemeDisplay;
51  import com.liferay.portal.tools.deploy.BaseDeployer;
52  import com.liferay.portal.upload.ProgressInputStream;
53  import com.liferay.portal.util.HttpImpl;
54  import com.liferay.portal.util.PortalUtil;
55  import com.liferay.portal.util.PrefsPropsUtil;
56  import com.liferay.portal.util.PropsKeys;
57  import com.liferay.portal.util.PropsUtil;
58  import com.liferay.portal.util.PropsValues;
59  import com.liferay.portal.util.WebKeys;
60  import com.liferay.util.servlet.UploadException;
61  
62  import java.io.File;
63  import java.io.FileOutputStream;
64  import java.io.IOException;
65  
66  import java.net.MalformedURLException;
67  import java.net.URL;
68  
69  import java.util.List;
70  
71  import javax.portlet.ActionRequest;
72  import javax.portlet.ActionResponse;
73  import javax.portlet.PortletConfig;
74  import javax.portlet.PortletPreferences;
75  
76  import javax.servlet.http.HttpServletResponse;
77  
78  import org.apache.commons.httpclient.HostConfiguration;
79  import org.apache.commons.httpclient.HttpClient;
80  import org.apache.commons.httpclient.methods.GetMethod;
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 preferences = PrefsPropsUtil.getPreferences();
168 
169         preferences.setValue(
170             PropsKeys.AUTO_DEPLOY_ENABLED, String.valueOf(enabled));
171         preferences.setValue(PropsKeys.AUTO_DEPLOY_DEPLOY_DIR, deployDir);
172         preferences.setValue(PropsKeys.AUTO_DEPLOY_DEST_DIR, destDir);
173         preferences.setValue(
174             PropsKeys.AUTO_DEPLOY_INTERVAL, String.valueOf(interval));
175         preferences.setValue(
176             PropsKeys.AUTO_DEPLOY_BLACKLIST_THRESHOLD,
177             String.valueOf(blacklistThreshold));
178         preferences.setValue(
179             PropsKeys.AUTO_DEPLOY_UNPACK_WAR, String.valueOf(unpackWar));
180         preferences.setValue(
181             PropsKeys.AUTO_DEPLOY_CUSTOM_PORTLET_XML,
182             String.valueOf(customPortletXml));
183         preferences.setValue(PropsKeys.AUTO_DEPLOY_JBOSS_PREFIX, jbossPrefix);
184         preferences.setValue(
185             PropsKeys.AUTO_DEPLOY_TOMCAT_CONF_DIR, tomcatConfDir);
186         preferences.setValue(
187             PropsKeys.AUTO_DEPLOY_TOMCAT_LIB_DIR, tomcatLibDir);
188         preferences.setValue(
189             PropsKeys.PLUGIN_REPOSITORIES_TRUSTED, pluginRepositoriesTrusted);
190         preferences.setValue(
191             PropsKeys.PLUGIN_REPOSITORIES_UNTRUSTED,
192             pluginRepositoriesUntrusted);
193         preferences.setValue(
194             PropsKeys.PLUGIN_NOTIFICATIONS_ENABLED,
195             String.valueOf(pluginNotificationsEnabled));
196         preferences.setValue(
197             PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
198             pluginPackagesIgnored);
199 
200         preferences.store();
201 
202         reloadRepositories(actionRequest);
203 
204         if (_log.isInfoEnabled()) {
205             _log.info("Unregistering auto deploy directories");
206         }
207 
208         AutoDeployUtil.unregisterDir("defaultAutoDeployDir");
209 
210         if (enabled) {
211             if (_log.isInfoEnabled()) {
212                 _log.info("Registering auto deploy directories");
213             }
214 
215             List<AutoDeployListener> autoDeployListeners =
216                 GlobalStartupAction.getAutoDeployListeners();
217 
218             AutoDeployDir autoDeployDir = new AutoDeployDir(
219                 "defaultAutoDeployDir", new File(deployDir), new File(destDir),
220                 interval, blacklistThreshold, autoDeployListeners);
221 
222             AutoDeployUtil.registerDir(autoDeployDir);
223         }
224         else {
225             if (_log.isInfoEnabled()) {
226                 _log.info("Not registering auto deploy directories");
227             }
228         }
229     }
230 
231     protected String[] getSourceForgeMirrors() {
232         return PropsUtil.getArray(PropsKeys.SOURCE_FORGE_MIRRORS);
233     }
234 
235     protected void ignorePackages(ActionRequest actionRequest)
236         throws Exception {
237 
238         String pluginPackagesIgnored = ParamUtil.getString(
239             actionRequest, "pluginPackagesIgnored");
240 
241         String oldPluginPackagesIgnored= PrefsPropsUtil.getString(
242             PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED);
243 
244         StringBuilder sb = new StringBuilder();
245 
246         sb.append(oldPluginPackagesIgnored);
247         sb.append(StringPool.NEW_LINE);
248         sb.append(pluginPackagesIgnored);
249 
250         PortletPreferences preferences = PrefsPropsUtil.getPreferences();
251 
252         preferences.setValue(
253             PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED, sb.toString());
254 
255         preferences.store();
256 
257         PluginPackageUtil.refreshUpdatesAvailableCache();
258     }
259 
260     protected void localDeploy(ActionRequest actionRequest) throws Exception {
261         UploadPortletRequest uploadRequest = PortalUtil.getUploadPortletRequest(
262             actionRequest);
263 
264         String fileName = null;
265 
266         String deploymentContext = ParamUtil.getString(
267             actionRequest, "deploymentContext");
268 
269         if (Validator.isNotNull(deploymentContext)) {
270             fileName =
271                 BaseDeployer.DEPLOY_TO_PREFIX + deploymentContext + ".war";
272         }
273         else {
274             fileName = GetterUtil.getString(uploadRequest.getFileName("file"));
275 
276             int pos = fileName.lastIndexOf(StringPool.PERIOD);
277 
278             if (pos != -1) {
279                 deploymentContext = fileName.substring(0, pos);
280             }
281         }
282 
283         File file = uploadRequest.getFile("file");
284 
285         byte[] bytes = FileUtil.getBytes(file);
286 
287         if ((bytes == null) || (bytes.length == 0)) {
288             SessionErrors.add(actionRequest, UploadException.class.getName());
289 
290             return;
291         }
292 
293         try {
294             PluginPackageUtil.registerPluginPackageInstallation(
295                 deploymentContext);
296 
297             String source = file.toString();
298 
299             String deployDir = PrefsPropsUtil.getString(
300                 PropsKeys.AUTO_DEPLOY_DEPLOY_DIR,
301                 PropsValues.AUTO_DEPLOY_DEPLOY_DIR);
302 
303             String destination = deployDir + StringPool.SLASH + fileName;
304 
305             FileUtil.copyFile(source, destination);
306 
307             SessionMessages.add(actionRequest, "pluginUploaded");
308         }
309         finally {
310             PluginPackageUtil.endPluginPackageInstallation(deploymentContext);
311         }
312     }
313 
314     protected void reloadRepositories(ActionRequest actionRequest)
315         throws Exception {
316 
317         RepositoryReport report = PluginPackageUtil.reloadRepositories();
318 
319         SessionMessages.add(
320             actionRequest, WebKeys.PLUGIN_REPOSITORY_REPORT, report);
321     }
322 
323     protected void remoteDeploy(ActionRequest actionRequest) throws Exception {
324         try {
325             String url = ParamUtil.getString(actionRequest, "url");
326 
327             URL urlObj = new URL(url);
328 
329             String host = urlObj.getHost();
330 
331             if (host.endsWith(".sf.net") || host.endsWith(".sourceforge.net")) {
332                 remoteDeploySourceForge(urlObj.getPath(), actionRequest);
333             }
334             else {
335                 remoteDeploy(url, urlObj, actionRequest, true);
336             }
337         }
338         catch (MalformedURLException murle) {
339             SessionErrors.add(actionRequest, "invalidUrl", murle);
340         }
341     }
342 
343     protected int remoteDeploy(
344             String url, URL urlObj, ActionRequest actionRequest,
345             boolean failOnError)
346         throws Exception {
347 
348         int responseCode = HttpServletResponse.SC_OK;
349 
350         GetMethod getMethod = null;
351 
352         String deploymentContext = ParamUtil.getString(
353             actionRequest, "deploymentContext");
354 
355         try {
356             HttpImpl httpImpl = (HttpImpl)HttpUtil.getHttp();
357 
358             HostConfiguration hostConfig = httpImpl.getHostConfig(url);
359 
360             HttpClient client = httpImpl.getClient(hostConfig);
361 
362             getMethod = new GetMethod(url);
363 
364             String fileName = null;
365 
366             if (Validator.isNotNull(deploymentContext)) {
367                 fileName =
368                     BaseDeployer.DEPLOY_TO_PREFIX + deploymentContext + ".war";
369             }
370             else {
371                 fileName = url.substring(url.lastIndexOf(StringPool.SLASH) + 1);
372 
373                 int pos = fileName.lastIndexOf(StringPool.PERIOD);
374 
375                 if (pos != -1) {
376                     deploymentContext = fileName.substring(0, pos);
377                 }
378             }
379 
380             PluginPackageUtil.registerPluginPackageInstallation(
381                 deploymentContext);
382 
383             responseCode = client.executeMethod(hostConfig, getMethod);
384 
385             if (responseCode != HttpServletResponse.SC_OK) {
386                 if (failOnError) {
387                     SessionErrors.add(
388                         actionRequest, "errorConnectingToUrl",
389                         new Object[] {String.valueOf(responseCode)});
390                 }
391 
392                 return responseCode;
393             }
394 
395             long contentLength = getMethod.getResponseContentLength();
396 
397             String progressId = ParamUtil.getString(
398                 actionRequest, Constants.PROGRESS_ID);
399 
400             ProgressInputStream pis = new ProgressInputStream(
401                 actionRequest, getMethod.getResponseBodyAsStream(),
402                 contentLength, progressId);
403 
404             String deployDir = PrefsPropsUtil.getString(
405                 PropsKeys.AUTO_DEPLOY_DEPLOY_DIR,
406                 PropsValues.AUTO_DEPLOY_DEPLOY_DIR);
407 
408             String tmpFilePath =
409                 deployDir + StringPool.SLASH + _DOWNLOAD_DIR +
410                     StringPool.SLASH + fileName;
411 
412             File tmpFile = new File(tmpFilePath);
413 
414             if (!tmpFile.getParentFile().exists()) {
415                 tmpFile.getParentFile().mkdirs();
416             }
417 
418             FileOutputStream fos = new FileOutputStream(tmpFile);
419 
420             try {
421                 pis.readAll(fos);
422 
423                 if (_log.isInfoEnabled()) {
424                     _log.info(
425                         "Downloaded plugin from " + urlObj + " has " +
426                             pis.getTotalRead() + " bytes");
427                 }
428             }
429             finally {
430                 pis.clearProgress();
431             }
432 
433             getMethod.releaseConnection();
434 
435             if (pis.getTotalRead() > 0) {
436                 String destination = deployDir + StringPool.SLASH + fileName;
437 
438                 File destinationFile = new File(destination);
439 
440                 boolean moved = FileUtil.move(tmpFile, destinationFile);
441 
442                 if (!moved) {
443                     FileUtil.copyFile(tmpFile, destinationFile);
444                     FileUtil.delete(tmpFile);
445                 }
446 
447                 SessionMessages.add(actionRequest, "pluginDownloaded");
448             }
449             else {
450                 if (failOnError) {
451                     SessionErrors.add(
452                         actionRequest, UploadException.class.getName());
453                 }
454 
455                 responseCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
456             }
457         }
458         catch (MalformedURLException murle) {
459             SessionErrors.add(actionRequest, "invalidUrl", murle);
460         }
461         catch (IOException ioe) {
462             SessionErrors.add(actionRequest, "errorConnectingToUrl", ioe);
463         }
464         finally {
465             if (getMethod != null) {
466                 getMethod.releaseConnection();
467             }
468 
469             PluginPackageUtil.endPluginPackageInstallation(deploymentContext);
470         }
471 
472         return responseCode;
473     }
474 
475     protected void remoteDeploySourceForge(
476             String path, ActionRequest actionRequest)
477         throws Exception {
478 
479         String[] sourceForgeMirrors = getSourceForgeMirrors();
480 
481         for (int i = 0; i < sourceForgeMirrors.length; i++) {
482             try {
483                 String url = sourceForgeMirrors[i] + path;
484 
485                 if (_log.isDebugEnabled()) {
486                     _log.debug("Downloading from SourceForge mirror " + url);
487                 }
488 
489                 URL urlObj = new URL(url);
490 
491                 boolean failOnError = false;
492 
493                 if ((i + 1) == sourceForgeMirrors.length) {
494                     failOnError = true;
495                 }
496 
497                 int responseCode = remoteDeploy(
498                     url, urlObj, actionRequest, failOnError);
499 
500                 if (responseCode == HttpServletResponse.SC_OK) {
501                     return;
502                 }
503             }
504             catch (MalformedURLException murle) {
505                 SessionErrors.add(actionRequest, "invalidUrl", murle);
506             }
507         }
508     }
509 
510     protected void unignorePackages(ActionRequest actionRequest)
511         throws Exception {
512 
513         String[] pluginPackagesUnignored = StringUtil.split(
514             ParamUtil.getString(actionRequest, "pluginPackagesUnignored"),
515             StringPool.NEW_LINE);
516 
517         String[] pluginPackagesIgnored = PrefsPropsUtil.getStringArray(
518             PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
519             StringPool.NEW_LINE,
520             PropsValues.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED);
521 
522         StringBuilder sb = new StringBuilder();
523 
524         for (int i = 0; i < pluginPackagesIgnored.length; i++) {
525             String packageId = pluginPackagesIgnored[i];
526 
527             if (!ArrayUtil.contains(pluginPackagesUnignored, packageId)) {
528                 sb.append(packageId);
529                 sb.append(StringPool.NEW_LINE);
530             }
531         }
532 
533         PortletPreferences preferences = PrefsPropsUtil.getPreferences();
534 
535         preferences.setValue(
536             PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED, sb.toString());
537 
538         preferences.store();
539 
540         PluginPackageUtil.refreshUpdatesAvailableCache();
541     }
542 
543     protected void uninstall(ActionRequest actionRequest) throws Exception {
544         String appServerType = ServerDetector.getServerId();
545 
546         String deploymentContext = ParamUtil.getString(
547             actionRequest, "deploymentContext");
548 
549         if (appServerType.startsWith(ServerDetector.JBOSS_ID)) {
550             deploymentContext += ".war";
551         }
552 
553         File deployDir = new File(
554             DeployUtil.getAutoDeployDestDir() + "/" + deploymentContext);
555 
556         DeployUtil.undeploy(appServerType, deployDir);
557     }
558 
559     private static final String _DOWNLOAD_DIR = "download";
560 
561     private static Log _log = LogFactoryUtil.getLog(InstallPluginAction.class);
562 
563 }