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.util.ArrayUtil;
31  import com.liferay.portal.kernel.util.Constants;
32  import com.liferay.portal.kernel.util.GetterUtil;
33  import com.liferay.portal.kernel.util.HttpUtil;
34  import com.liferay.portal.kernel.util.ParamUtil;
35  import com.liferay.portal.kernel.util.ServerDetector;
36  import com.liferay.portal.kernel.util.StringMaker;
37  import com.liferay.portal.kernel.util.StringPool;
38  import com.liferay.portal.kernel.util.StringUtil;
39  import com.liferay.portal.kernel.util.Validator;
40  import com.liferay.portal.plugin.PluginPackageUtil;
41  import com.liferay.portal.plugin.RepositoryReport;
42  import com.liferay.portal.security.auth.PrincipalException;
43  import com.liferay.portal.security.permission.PermissionChecker;
44  import com.liferay.portal.struts.PortletAction;
45  import com.liferay.portal.theme.ThemeDisplay;
46  import com.liferay.portal.tools.BaseDeployer;
47  import com.liferay.portal.util.HttpImpl;
48  import com.liferay.portal.util.PrefsPropsUtil;
49  import com.liferay.portal.util.PropsUtil;
50  import com.liferay.portal.util.PropsValues;
51  import com.liferay.portal.util.UploadRequestUtil;
52  import com.liferay.portal.util.WebKeys;
53  import com.liferay.util.FileUtil;
54  import com.liferay.util.servlet.ProgressInputStream;
55  import com.liferay.util.servlet.SessionErrors;
56  import com.liferay.util.servlet.SessionMessages;
57  import com.liferay.util.servlet.UploadException;
58  import com.liferay.util.servlet.UploadPortletRequest;
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 config,
96              ActionRequest req, ActionResponse res)
97          throws Exception {
98  
99          ThemeDisplay themeDisplay =
100             (ThemeDisplay)req.getAttribute(WebKeys.THEME_DISPLAY);
101 
102         PermissionChecker permissionChecker =
103             themeDisplay.getPermissionChecker();
104 
105         if (!permissionChecker.isOmniadmin()) {
106             SessionErrors.add(req, PrincipalException.class.getName());
107 
108             setForward(req, "portlet.plugin_installer.error");
109 
110             return;
111         }
112 
113         String cmd = ParamUtil.getString(req, Constants.CMD);
114 
115         if (cmd.equals("deployConfiguration")) {
116             deployConfiguration(req);
117         }
118         else if (cmd.equals("ignorePackages")) {
119             ignorePackages(req);
120         }
121         else if (cmd.equals("localDeploy")) {
122             localDeploy(req);
123         }
124         else if (cmd.equals("reloadRepositories")) {
125             reloadRepositories(req);
126         }
127         else if (cmd.equals("remoteDeploy")) {
128             remoteDeploy(req);
129         }
130         else if (cmd.equals("unignorePackages")) {
131             unignorePackages(req);
132         }
133         else if (cmd.equals("uninstall")) {
134             uninstall(req);
135         }
136 
137         sendRedirect(req, res);
138     }
139 
140     protected void deployConfiguration(ActionRequest req) throws Exception {
141         boolean enabled = ParamUtil.getBoolean(req, "enabled");
142         String deployDir = ParamUtil.getString(req, "deployDir");
143         String destDir = ParamUtil.getString(req, "destDir");
144         long interval = ParamUtil.getLong(req, "interval");
145         int blacklistThreshold = ParamUtil.getInteger(
146             req, "blacklistThreshold");
147         boolean unpackWar = ParamUtil.getBoolean(req, "unpackWar");
148         boolean customPortletXml = ParamUtil.getBoolean(
149             req, "customPortletXml");
150         String jbossPrefix = ParamUtil.getString(req, "jbossPrefix");
151         String tomcatConfDir = ParamUtil.getString(req, "tomcatConfDir");
152         String tomcatLibDir = ParamUtil.getString(req, "tomcatLibDir");
153         String pluginRepositoriesTrusted = ParamUtil.getString(
154             req, "pluginRepositoriesTrusted");
155         String pluginRepositoriesUntrusted = ParamUtil.getString(
156             req, "pluginRepositoriesUntrusted");
157         boolean pluginNotificationsEnabled = ParamUtil.getBoolean(
158             req, "pluginNotificationsEnabled");
159         String pluginPackagesIgnored = ParamUtil.getString(
160             req, "pluginPackagesIgnored");
161 
162         PortletPreferences prefs = PrefsPropsUtil.getPreferences();
163 
164         prefs.setValue(PropsUtil.AUTO_DEPLOY_ENABLED, String.valueOf(enabled));
165         prefs.setValue(PropsUtil.AUTO_DEPLOY_DEPLOY_DIR, deployDir);
166         prefs.setValue(PropsUtil.AUTO_DEPLOY_DEST_DIR, destDir);
167         prefs.setValue(
168             PropsUtil.AUTO_DEPLOY_INTERVAL, String.valueOf(interval));
169         prefs.setValue(
170             PropsUtil.AUTO_DEPLOY_BLACKLIST_THRESHOLD,
171             String.valueOf(blacklistThreshold));
172         prefs.setValue(
173             PropsUtil.AUTO_DEPLOY_UNPACK_WAR, String.valueOf(unpackWar));
174         prefs.setValue(
175             PropsUtil.AUTO_DEPLOY_CUSTOM_PORTLET_XML,
176             String.valueOf(customPortletXml));
177         prefs.setValue(PropsUtil.AUTO_DEPLOY_JBOSS_PREFIX, jbossPrefix);
178         prefs.setValue(PropsUtil.AUTO_DEPLOY_TOMCAT_CONF_DIR, tomcatConfDir);
179         prefs.setValue(PropsUtil.AUTO_DEPLOY_TOMCAT_LIB_DIR, tomcatLibDir);
180         prefs.setValue(
181             PropsUtil.PLUGIN_REPOSITORIES_TRUSTED, pluginRepositoriesTrusted);
182         prefs.setValue(
183             PropsUtil.PLUGIN_REPOSITORIES_UNTRUSTED,
184             pluginRepositoriesUntrusted);
185         prefs.setValue(
186             PropsUtil.PLUGIN_NOTIFICATIONS_ENABLED,
187             String.valueOf(pluginNotificationsEnabled));
188         prefs.setValue(
189             PropsUtil.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
190             pluginPackagesIgnored);
191 
192         prefs.store();
193 
194         reloadRepositories(req);
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(PropsUtil.SOURCE_FORGE_MIRRORS);
225     }
226 
227     protected void ignorePackages(ActionRequest req) throws Exception {
228         String pluginPackagesIgnored = ParamUtil.getString(
229             req, "pluginPackagesIgnored");
230 
231         String oldPluginPackagesIgnored= PrefsPropsUtil.getString(
232             PropsUtil.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED);
233 
234         StringMaker sm = new StringMaker();
235 
236         sm.append(oldPluginPackagesIgnored);
237         sm.append(StringPool.NEW_LINE);
238         sm.append(pluginPackagesIgnored);
239 
240         PortletPreferences prefs = PrefsPropsUtil.getPreferences();
241 
242         prefs.setValue(
243             PropsUtil.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED, sm.toString());
244 
245         prefs.store();
246 
247         PluginPackageUtil.refreshUpdatesAvailableCache();
248     }
249 
250     protected void localDeploy(ActionRequest req) throws Exception {
251         UploadPortletRequest uploadReq =
252             UploadRequestUtil.getUploadPortletRequest(req);
253 
254         String fileName = null;
255 
256         String deploymentContext = ParamUtil.getString(
257             req, "deploymentContext");
258 
259         if (Validator.isNotNull(deploymentContext)) {
260             fileName =
261                 BaseDeployer.DEPLOY_TO_PREFIX + deploymentContext + ".war";
262         }
263         else {
264             fileName = GetterUtil.getString(uploadReq.getFileName("file"));
265 
266             int pos = fileName.lastIndexOf(StringPool.PERIOD);
267 
268             if (pos != -1) {
269                 deploymentContext = fileName.substring(0, pos);
270             }
271         }
272 
273         File file = uploadReq.getFile("file");
274 
275         byte[] bytes = FileUtil.getBytes(file);
276 
277         if ((bytes == null) || (bytes.length == 0)) {
278             SessionErrors.add(req, UploadException.class.getName());
279 
280             return;
281         }
282 
283         try {
284             PluginPackageUtil.registerPluginPackageInstallation(
285                 deploymentContext);
286 
287             String source = file.toString();
288 
289             String deployDir = PrefsPropsUtil.getString(
290                 PropsUtil.AUTO_DEPLOY_DEPLOY_DIR,
291                 PropsValues.AUTO_DEPLOY_DEPLOY_DIR);
292 
293             String destination = deployDir + StringPool.SLASH + fileName;
294 
295             FileUtil.copyFile(source, destination);
296 
297             SessionMessages.add(req, "pluginUploaded");
298         }
299         finally {
300             PluginPackageUtil.endPluginPackageInstallation(deploymentContext);
301         }
302     }
303 
304     protected void reloadRepositories(ActionRequest req) throws Exception {
305         RepositoryReport report = PluginPackageUtil.reloadRepositories();
306 
307         SessionMessages.add(req, WebKeys.PLUGIN_REPOSITORY_REPORT, report);
308     }
309 
310     protected void remoteDeploy(ActionRequest req) throws Exception {
311         try {
312             String url = ParamUtil.getString(req, "url");
313 
314             URL urlObj = new URL(url);
315 
316             String host = urlObj.getHost();
317 
318             if (host.endsWith(".sf.net") || host.endsWith(".sourceforge.net")) {
319                 remoteDeploySourceForge(urlObj.getPath(), req);
320             }
321             else {
322                 remoteDeploy(url, urlObj, req, true);
323             }
324         }
325         catch (MalformedURLException murle) {
326             SessionErrors.add(req, "invalidUrl", murle);
327         }
328     }
329 
330     protected int remoteDeploy(
331             String url, URL urlObj, ActionRequest req, boolean failOnError)
332         throws Exception {
333 
334         int responseCode = HttpServletResponse.SC_OK;
335 
336         GetMethod getMethod = null;
337 
338         String deploymentContext = ParamUtil.getString(
339             req, "deploymentContext");
340 
341         try {
342             HttpImpl httpImpl = (HttpImpl)HttpUtil.getHttp();
343 
344             HostConfiguration hostConfig = httpImpl.getHostConfig(url);
345 
346             HttpClient client = httpImpl.getClient(hostConfig);
347 
348             getMethod = new GetMethod(url);
349 
350             String fileName = null;
351 
352             if (Validator.isNotNull(deploymentContext)) {
353                 fileName =
354                     BaseDeployer.DEPLOY_TO_PREFIX + deploymentContext + ".war";
355             }
356             else {
357                 fileName = url.substring(url.lastIndexOf(StringPool.SLASH) + 1);
358 
359                 int pos = fileName.lastIndexOf(StringPool.PERIOD);
360 
361                 if (pos != -1) {
362                     deploymentContext = fileName.substring(0, pos);
363                 }
364             }
365 
366             PluginPackageUtil.registerPluginPackageInstallation(
367                 deploymentContext);
368 
369             responseCode = client.executeMethod(hostConfig, getMethod);
370 
371             if (responseCode != HttpServletResponse.SC_OK) {
372                 if (failOnError) {
373                     SessionErrors.add(
374                         req, "errorConnectingToUrl",
375                         new Object[] {String.valueOf(responseCode)});
376                 }
377 
378                 return responseCode;
379             }
380 
381             long contentLength = getMethod.getResponseContentLength();
382 
383             String progressId = ParamUtil.getString(req, Constants.PROGRESS_ID);
384 
385             ProgressInputStream pis = new ProgressInputStream(
386                 req, getMethod.getResponseBodyAsStream(), contentLength,
387                 progressId);
388 
389             String deployDir = PrefsPropsUtil.getString(
390                 PropsUtil.AUTO_DEPLOY_DEPLOY_DIR,
391                 PropsValues.AUTO_DEPLOY_DEPLOY_DIR);
392 
393             String tmpFilePath =
394                 deployDir + StringPool.SLASH + _DOWNLOAD_DIR +
395                     StringPool.SLASH + fileName;
396 
397             File tmpFile = new File(tmpFilePath);
398 
399             if (!tmpFile.getParentFile().exists()) {
400                 tmpFile.getParentFile().mkdirs();
401             }
402 
403             FileOutputStream fos = new FileOutputStream(tmpFile);
404 
405             try {
406                 pis.readAll(fos);
407 
408                 if (_log.isInfoEnabled()) {
409                     _log.info(
410                         "Downloaded plugin from " + urlObj + " has " +
411                             pis.getTotalRead() + " bytes");
412                 }
413             }
414             finally {
415                 pis.clearProgress();
416             }
417 
418             getMethod.releaseConnection();
419 
420             if (pis.getTotalRead() > 0) {
421                 String destination = deployDir + StringPool.SLASH + fileName;
422 
423                 File destinationFile = new File(destination);
424 
425                 boolean moved = FileUtil.move(tmpFile, destinationFile);
426 
427                 if (!moved) {
428                     FileUtil.copyFile(tmpFile, destinationFile);
429                     FileUtil.delete(tmpFile);
430                 }
431 
432                 SessionMessages.add(req, "pluginDownloaded");
433             }
434             else {
435                 if (failOnError) {
436                     SessionErrors.add(req, UploadException.class.getName());
437                 }
438 
439                 responseCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
440             }
441         }
442         catch (MalformedURLException murle) {
443             SessionErrors.add(req, "invalidUrl", murle);
444         }
445         catch (IOException ioe) {
446             SessionErrors.add(req, "errorConnectingToUrl", ioe);
447         }
448         finally {
449             if (getMethod != null) {
450                 getMethod.releaseConnection();
451             }
452 
453             PluginPackageUtil.endPluginPackageInstallation(deploymentContext);
454         }
455 
456         return responseCode;
457     }
458 
459     protected void remoteDeploySourceForge(String path, ActionRequest req)
460         throws Exception {
461 
462         String[] sourceForgeMirrors = getSourceForgeMirrors();
463 
464         for (int i = 0; i < sourceForgeMirrors.length; i++) {
465             try {
466                 String url = sourceForgeMirrors[i] + path;
467 
468                 if (_log.isDebugEnabled()) {
469                     _log.debug("Downloading from SourceForge mirror " + url);
470                 }
471 
472                 URL urlObj = new URL(url);
473 
474                 boolean failOnError = false;
475 
476                 if ((i + 1) == sourceForgeMirrors.length) {
477                     failOnError = true;
478                 }
479 
480                 int responseCode = remoteDeploy(url, urlObj, req, failOnError);
481 
482                 if (responseCode == HttpServletResponse.SC_OK) {
483                     return;
484                 }
485             }
486             catch (MalformedURLException murle) {
487                 SessionErrors.add(req, "invalidUrl", murle);
488             }
489         }
490     }
491 
492     protected void unignorePackages(ActionRequest req) throws Exception {
493         String[] pluginPackagesUnignored = StringUtil.split(
494             ParamUtil.getString(req, "pluginPackagesUnignored"),
495             StringPool.NEW_LINE);
496 
497         String[] pluginPackagesIgnored = PrefsPropsUtil.getStringArray(
498             PropsUtil.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
499             StringPool.NEW_LINE,
500             PropsValues.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED);
501 
502         StringMaker sm = new StringMaker();
503 
504         for (int i = 0; i < pluginPackagesIgnored.length; i++) {
505             String packageId = pluginPackagesIgnored[i];
506 
507             if (!ArrayUtil.contains(pluginPackagesUnignored, packageId)) {
508                 sm.append(packageId);
509                 sm.append(StringPool.NEW_LINE);
510             }
511         }
512 
513         PortletPreferences prefs = PrefsPropsUtil.getPreferences();
514 
515         prefs.setValue(
516             PropsUtil.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED, sm.toString());
517 
518         prefs.store();
519 
520         PluginPackageUtil.refreshUpdatesAvailableCache();
521     }
522 
523     protected void uninstall(ActionRequest req) throws Exception {
524         String appServerType = ServerDetector.getServerId();
525 
526         String deploymentContext = ParamUtil.getString(
527             req, "deploymentContext");
528 
529         if (appServerType.startsWith(ServerDetector.JBOSS_ID)) {
530             deploymentContext += ".war";
531         }
532 
533         File deployDir = new File(
534             DeployUtil.getAutoDeployDestDir() + "/" + deploymentContext);
535 
536         DeployUtil.undeploy(appServerType, deployDir);
537     }
538 
539     private static final String _DOWNLOAD_DIR = "download";
540 
541     private static Log _log = LogFactory.getLog(InstallPluginAction.class);
542 
543 }