1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    *
5    *
6    *
7    * The contents of this file are subject to the terms of the Liferay Enterprise
8    * Subscription License ("License"). You may not use this file except in
9    * compliance with the License. You can obtain a copy of the License by
10   * contacting Liferay, Inc. See the License for the specific language governing
11   * permissions and limitations under the License, including but not limited to
12   * distribution rights 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.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  public class InstallPluginAction extends PortletAction {
92  
93      public void processAction(
94              ActionMapping mapping, ActionForm form, PortletConfig portletConfig,
95              ActionRequest actionRequest, ActionResponse actionResponse)
96          throws Exception {
97  
98          ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute(
99              WebKeys.THEME_DISPLAY);
100 
101         PermissionChecker permissionChecker =
102             themeDisplay.getPermissionChecker();
103 
104         if (!permissionChecker.isOmniadmin()) {
105             SessionErrors.add(
106                 actionRequest, PrincipalException.class.getName());
107 
108             setForward(actionRequest, "portlet.plugin_installer.error");
109 
110             return;
111         }
112 
113         String cmd = ParamUtil.getString(actionRequest, Constants.CMD);
114 
115         if (cmd.equals("deployConfiguration")) {
116             deployConfiguration(actionRequest);
117         }
118         else if (cmd.equals("ignorePackages")) {
119             ignorePackages(actionRequest);
120         }
121         else if (cmd.equals("localDeploy")) {
122             localDeploy(actionRequest);
123         }
124         else if (cmd.equals("reloadRepositories")) {
125             reloadRepositories(actionRequest);
126         }
127         else if (cmd.equals("remoteDeploy")) {
128             remoteDeploy(actionRequest);
129         }
130         else if (cmd.equals("unignorePackages")) {
131             unignorePackages(actionRequest);
132         }
133         else if (cmd.equals("uninstall")) {
134             uninstall(actionRequest);
135         }
136 
137         sendRedirect(actionRequest, actionResponse);
138     }
139 
140     protected void deployConfiguration(ActionRequest actionRequest)
141         throws Exception {
142 
143         boolean enabled = ParamUtil.getBoolean(actionRequest, "enabled");
144         String deployDir = ParamUtil.getString(actionRequest, "deployDir");
145         String destDir = ParamUtil.getString(actionRequest, "destDir");
146         long interval = ParamUtil.getLong(actionRequest, "interval");
147         int blacklistThreshold = ParamUtil.getInteger(
148             actionRequest, "blacklistThreshold");
149         boolean unpackWar = ParamUtil.getBoolean(actionRequest, "unpackWar");
150         boolean customPortletXml = ParamUtil.getBoolean(
151             actionRequest, "customPortletXml");
152         String jbossPrefix = ParamUtil.getString(actionRequest, "jbossPrefix");
153         String tomcatConfDir = ParamUtil.getString(
154             actionRequest, "tomcatConfDir");
155         String tomcatLibDir = ParamUtil.getString(
156             actionRequest, "tomcatLibDir");
157         String pluginRepositoriesTrusted = ParamUtil.getString(
158             actionRequest, "pluginRepositoriesTrusted");
159         String pluginRepositoriesUntrusted = ParamUtil.getString(
160             actionRequest, "pluginRepositoriesUntrusted");
161         boolean pluginNotificationsEnabled = ParamUtil.getBoolean(
162             actionRequest, "pluginNotificationsEnabled");
163         String pluginPackagesIgnored = ParamUtil.getString(
164             actionRequest, "pluginPackagesIgnored");
165 
166         PortletPreferences prefs = PrefsPropsUtil.getPreferences();
167 
168         prefs.setValue(PropsKeys.AUTO_DEPLOY_ENABLED, String.valueOf(enabled));
169         prefs.setValue(PropsKeys.AUTO_DEPLOY_DEPLOY_DIR, deployDir);
170         prefs.setValue(PropsKeys.AUTO_DEPLOY_DEST_DIR, destDir);
171         prefs.setValue(
172             PropsKeys.AUTO_DEPLOY_INTERVAL, String.valueOf(interval));
173         prefs.setValue(
174             PropsKeys.AUTO_DEPLOY_BLACKLIST_THRESHOLD,
175             String.valueOf(blacklistThreshold));
176         prefs.setValue(
177             PropsKeys.AUTO_DEPLOY_UNPACK_WAR, String.valueOf(unpackWar));
178         prefs.setValue(
179             PropsKeys.AUTO_DEPLOY_CUSTOM_PORTLET_XML,
180             String.valueOf(customPortletXml));
181         prefs.setValue(PropsKeys.AUTO_DEPLOY_JBOSS_PREFIX, jbossPrefix);
182         prefs.setValue(PropsKeys.AUTO_DEPLOY_TOMCAT_CONF_DIR, tomcatConfDir);
183         prefs.setValue(PropsKeys.AUTO_DEPLOY_TOMCAT_LIB_DIR, tomcatLibDir);
184         prefs.setValue(
185             PropsKeys.PLUGIN_REPOSITORIES_TRUSTED, pluginRepositoriesTrusted);
186         prefs.setValue(
187             PropsKeys.PLUGIN_REPOSITORIES_UNTRUSTED,
188             pluginRepositoriesUntrusted);
189         prefs.setValue(
190             PropsKeys.PLUGIN_NOTIFICATIONS_ENABLED,
191             String.valueOf(pluginNotificationsEnabled));
192         prefs.setValue(
193             PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
194             pluginPackagesIgnored);
195 
196         prefs.store();
197 
198         reloadRepositories(actionRequest);
199 
200         if (_log.isInfoEnabled()) {
201             _log.info("Unregistering auto deploy directories");
202         }
203 
204         AutoDeployUtil.unregisterDir("defaultAutoDeployDir");
205 
206         if (enabled) {
207             if (_log.isInfoEnabled()) {
208                 _log.info("Registering auto deploy directories");
209             }
210 
211             List<AutoDeployListener> autoDeployListeners =
212                 GlobalStartupAction.getAutoDeployListeners();
213 
214             AutoDeployDir autoDeployDir = new AutoDeployDir(
215                 "defaultAutoDeployDir", new File(deployDir), new File(destDir),
216                 interval, blacklistThreshold, autoDeployListeners);
217 
218             AutoDeployUtil.registerDir(autoDeployDir);
219         }
220         else {
221             if (_log.isInfoEnabled()) {
222                 _log.info("Not registering auto deploy directories");
223             }
224         }
225     }
226 
227     protected String[] getSourceForgeMirrors() {
228         return PropsUtil.getArray(PropsKeys.SOURCE_FORGE_MIRRORS);
229     }
230 
231     protected void ignorePackages(ActionRequest actionRequest)
232         throws Exception {
233 
234         String pluginPackagesIgnored = ParamUtil.getString(
235             actionRequest, "pluginPackagesIgnored");
236 
237         String oldPluginPackagesIgnored= PrefsPropsUtil.getString(
238             PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED);
239 
240         StringBuilder sb = new StringBuilder();
241 
242         sb.append(oldPluginPackagesIgnored);
243         sb.append(StringPool.NEW_LINE);
244         sb.append(pluginPackagesIgnored);
245 
246         PortletPreferences prefs = PrefsPropsUtil.getPreferences();
247 
248         prefs.setValue(
249             PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED, sb.toString());
250 
251         prefs.store();
252 
253         PluginPackageUtil.refreshUpdatesAvailableCache();
254     }
255 
256     protected void localDeploy(ActionRequest actionRequest) throws Exception {
257         UploadPortletRequest uploadRequest = PortalUtil.getUploadPortletRequest(
258             actionRequest);
259 
260         String fileName = null;
261 
262         String deploymentContext = ParamUtil.getString(
263             actionRequest, "deploymentContext");
264 
265         if (Validator.isNotNull(deploymentContext)) {
266             fileName =
267                 BaseDeployer.DEPLOY_TO_PREFIX + deploymentContext + ".war";
268         }
269         else {
270             fileName = GetterUtil.getString(uploadRequest.getFileName("file"));
271 
272             int pos = fileName.lastIndexOf(StringPool.PERIOD);
273 
274             if (pos != -1) {
275                 deploymentContext = fileName.substring(0, pos);
276             }
277         }
278 
279         File file = uploadRequest.getFile("file");
280 
281         byte[] bytes = FileUtil.getBytes(file);
282 
283         if ((bytes == null) || (bytes.length == 0)) {
284             SessionErrors.add(actionRequest, UploadException.class.getName());
285 
286             return;
287         }
288 
289         try {
290             PluginPackageUtil.registerPluginPackageInstallation(
291                 deploymentContext);
292 
293             String source = file.toString();
294 
295             String deployDir = PrefsPropsUtil.getString(
296                 PropsKeys.AUTO_DEPLOY_DEPLOY_DIR,
297                 PropsValues.AUTO_DEPLOY_DEPLOY_DIR);
298 
299             String destination = deployDir + StringPool.SLASH + fileName;
300 
301             FileUtil.copyFile(source, destination);
302 
303             SessionMessages.add(actionRequest, "pluginUploaded");
304         }
305         finally {
306             PluginPackageUtil.endPluginPackageInstallation(deploymentContext);
307         }
308     }
309 
310     protected void reloadRepositories(ActionRequest actionRequest)
311         throws Exception {
312 
313         RepositoryReport report = PluginPackageUtil.reloadRepositories();
314 
315         SessionMessages.add(
316             actionRequest, WebKeys.PLUGIN_REPOSITORY_REPORT, report);
317     }
318 
319     protected void remoteDeploy(ActionRequest actionRequest) throws Exception {
320         try {
321             String url = ParamUtil.getString(actionRequest, "url");
322 
323             URL urlObj = new URL(url);
324 
325             String host = urlObj.getHost();
326 
327             if (host.endsWith(".sf.net") || host.endsWith(".sourceforge.net")) {
328                 remoteDeploySourceForge(urlObj.getPath(), actionRequest);
329             }
330             else {
331                 remoteDeploy(url, urlObj, actionRequest, true);
332             }
333         }
334         catch (MalformedURLException murle) {
335             SessionErrors.add(actionRequest, "invalidUrl", murle);
336         }
337     }
338 
339     protected int remoteDeploy(
340             String url, URL urlObj, ActionRequest actionRequest,
341             boolean failOnError)
342         throws Exception {
343 
344         int responseCode = HttpServletResponse.SC_OK;
345 
346         GetMethod getMethod = null;
347 
348         String deploymentContext = ParamUtil.getString(
349             actionRequest, "deploymentContext");
350 
351         try {
352             HttpImpl httpImpl = (HttpImpl)HttpUtil.getHttp();
353 
354             HostConfiguration hostConfig = httpImpl.getHostConfig(url);
355 
356             HttpClient client = httpImpl.getClient(hostConfig);
357 
358             getMethod = new GetMethod(url);
359 
360             String fileName = null;
361 
362             if (Validator.isNotNull(deploymentContext)) {
363                 fileName =
364                     BaseDeployer.DEPLOY_TO_PREFIX + deploymentContext + ".war";
365             }
366             else {
367                 fileName = url.substring(url.lastIndexOf(StringPool.SLASH) + 1);
368 
369                 int pos = fileName.lastIndexOf(StringPool.PERIOD);
370 
371                 if (pos != -1) {
372                     deploymentContext = fileName.substring(0, pos);
373                 }
374             }
375 
376             PluginPackageUtil.registerPluginPackageInstallation(
377                 deploymentContext);
378 
379             responseCode = client.executeMethod(hostConfig, getMethod);
380 
381             if (responseCode != HttpServletResponse.SC_OK) {
382                 if (failOnError) {
383                     SessionErrors.add(
384                         actionRequest, "errorConnectingToUrl",
385                         new Object[] {String.valueOf(responseCode)});
386                 }
387 
388                 return responseCode;
389             }
390 
391             long contentLength = getMethod.getResponseContentLength();
392 
393             String progressId = ParamUtil.getString(
394                 actionRequest, Constants.PROGRESS_ID);
395 
396             ProgressInputStream pis = new ProgressInputStream(
397                 actionRequest, getMethod.getResponseBodyAsStream(),
398                 contentLength, progressId);
399 
400             String deployDir = PrefsPropsUtil.getString(
401                 PropsKeys.AUTO_DEPLOY_DEPLOY_DIR,
402                 PropsValues.AUTO_DEPLOY_DEPLOY_DIR);
403 
404             String tmpFilePath =
405                 deployDir + StringPool.SLASH + _DOWNLOAD_DIR +
406                     StringPool.SLASH + fileName;
407 
408             File tmpFile = new File(tmpFilePath);
409 
410             if (!tmpFile.getParentFile().exists()) {
411                 tmpFile.getParentFile().mkdirs();
412             }
413 
414             FileOutputStream fos = new FileOutputStream(tmpFile);
415 
416             try {
417                 pis.readAll(fos);
418 
419                 if (_log.isInfoEnabled()) {
420                     _log.info(
421                         "Downloaded plugin from " + urlObj + " has " +
422                             pis.getTotalRead() + " bytes");
423                 }
424             }
425             finally {
426                 pis.clearProgress();
427             }
428 
429             getMethod.releaseConnection();
430 
431             if (pis.getTotalRead() > 0) {
432                 String destination = deployDir + StringPool.SLASH + fileName;
433 
434                 File destinationFile = new File(destination);
435 
436                 boolean moved = FileUtil.move(tmpFile, destinationFile);
437 
438                 if (!moved) {
439                     FileUtil.copyFile(tmpFile, destinationFile);
440                     FileUtil.delete(tmpFile);
441                 }
442 
443                 SessionMessages.add(actionRequest, "pluginDownloaded");
444             }
445             else {
446                 if (failOnError) {
447                     SessionErrors.add(
448                         actionRequest, UploadException.class.getName());
449                 }
450 
451                 responseCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
452             }
453         }
454         catch (MalformedURLException murle) {
455             SessionErrors.add(actionRequest, "invalidUrl", murle);
456         }
457         catch (IOException ioe) {
458             SessionErrors.add(actionRequest, "errorConnectingToUrl", ioe);
459         }
460         finally {
461             if (getMethod != null) {
462                 getMethod.releaseConnection();
463             }
464 
465             PluginPackageUtil.endPluginPackageInstallation(deploymentContext);
466         }
467 
468         return responseCode;
469     }
470 
471     protected void remoteDeploySourceForge(
472             String path, ActionRequest actionRequest)
473         throws Exception {
474 
475         String[] sourceForgeMirrors = getSourceForgeMirrors();
476 
477         for (int i = 0; i < sourceForgeMirrors.length; i++) {
478             try {
479                 String url = sourceForgeMirrors[i] + path;
480 
481                 if (_log.isDebugEnabled()) {
482                     _log.debug("Downloading from SourceForge mirror " + url);
483                 }
484 
485                 URL urlObj = new URL(url);
486 
487                 boolean failOnError = false;
488 
489                 if ((i + 1) == sourceForgeMirrors.length) {
490                     failOnError = true;
491                 }
492 
493                 int responseCode = remoteDeploy(
494                     url, urlObj, actionRequest, failOnError);
495 
496                 if (responseCode == HttpServletResponse.SC_OK) {
497                     return;
498                 }
499             }
500             catch (MalformedURLException murle) {
501                 SessionErrors.add(actionRequest, "invalidUrl", murle);
502             }
503         }
504     }
505 
506     protected void unignorePackages(ActionRequest actionRequest)
507         throws Exception {
508 
509         String[] pluginPackagesUnignored = StringUtil.split(
510             ParamUtil.getString(actionRequest, "pluginPackagesUnignored"),
511             StringPool.NEW_LINE);
512 
513         String[] pluginPackagesIgnored = PrefsPropsUtil.getStringArray(
514             PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
515             StringPool.NEW_LINE,
516             PropsValues.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED);
517 
518         StringBuilder sb = new StringBuilder();
519 
520         for (int i = 0; i < pluginPackagesIgnored.length; i++) {
521             String packageId = pluginPackagesIgnored[i];
522 
523             if (!ArrayUtil.contains(pluginPackagesUnignored, packageId)) {
524                 sb.append(packageId);
525                 sb.append(StringPool.NEW_LINE);
526             }
527         }
528 
529         PortletPreferences prefs = PrefsPropsUtil.getPreferences();
530 
531         prefs.setValue(
532             PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED, sb.toString());
533 
534         prefs.store();
535 
536         PluginPackageUtil.refreshUpdatesAvailableCache();
537     }
538 
539     protected void uninstall(ActionRequest actionRequest) throws Exception {
540         String appServerType = ServerDetector.getServerId();
541 
542         String deploymentContext = ParamUtil.getString(
543             actionRequest, "deploymentContext");
544 
545         if (appServerType.startsWith(ServerDetector.JBOSS_ID)) {
546             deploymentContext += ".war";
547         }
548 
549         File deployDir = new File(
550             DeployUtil.getAutoDeployDestDir() + "/" + deploymentContext);
551 
552         DeployUtil.undeploy(appServerType, deployDir);
553     }
554 
555     private static final String _DOWNLOAD_DIR = "download";
556 
557     private static Log _log = LogFactoryUtil.getLog(InstallPluginAction.class);
558 
559 }