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