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