001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet;
016    
017    import com.liferay.portal.kernel.exception.SystemException;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.portlet.FriendlyURLMapper;
021    import com.liferay.portal.kernel.portlet.LiferayPortletConfig;
022    import com.liferay.portal.kernel.portlet.LiferayPortletURL;
023    import com.liferay.portal.kernel.portlet.LiferayWindowState;
024    import com.liferay.portal.kernel.portlet.PortletModeFactory;
025    import com.liferay.portal.kernel.portlet.WindowStateFactory;
026    import com.liferay.portal.kernel.util.ArrayUtil;
027    import com.liferay.portal.kernel.util.Base64;
028    import com.liferay.portal.kernel.util.CharPool;
029    import com.liferay.portal.kernel.util.GetterUtil;
030    import com.liferay.portal.kernel.util.HtmlUtil;
031    import com.liferay.portal.kernel.util.Http;
032    import com.liferay.portal.kernel.util.HttpUtil;
033    import com.liferay.portal.kernel.util.MapUtil;
034    import com.liferay.portal.kernel.util.ParamUtil;
035    import com.liferay.portal.kernel.util.StringBundler;
036    import com.liferay.portal.kernel.util.StringPool;
037    import com.liferay.portal.kernel.util.Validator;
038    import com.liferay.portal.kernel.xml.QName;
039    import com.liferay.portal.model.Company;
040    import com.liferay.portal.model.Layout;
041    import com.liferay.portal.model.Portlet;
042    import com.liferay.portal.model.PortletApp;
043    import com.liferay.portal.model.PublicRenderParameter;
044    import com.liferay.portal.model.impl.VirtualLayout;
045    import com.liferay.portal.security.auth.AuthTokenUtil;
046    import com.liferay.portal.security.lang.DoPrivilegedUtil;
047    import com.liferay.portal.service.LayoutLocalServiceUtil;
048    import com.liferay.portal.service.PortletLocalServiceUtil;
049    import com.liferay.portal.theme.PortletDisplay;
050    import com.liferay.portal.theme.ThemeDisplay;
051    import com.liferay.portal.util.CookieKeys;
052    import com.liferay.portal.util.PortalUtil;
053    import com.liferay.portal.util.PortletKeys;
054    import com.liferay.portal.util.PropsValues;
055    import com.liferay.portal.util.WebKeys;
056    import com.liferay.portlet.social.util.FacebookUtil;
057    import com.liferay.util.Encryptor;
058    import com.liferay.util.EncryptorException;
059    
060    import java.io.IOException;
061    import java.io.Serializable;
062    import java.io.UnsupportedEncodingException;
063    import java.io.Writer;
064    
065    import java.security.Key;
066    import java.security.PrivilegedAction;
067    
068    import java.util.Collections;
069    import java.util.Iterator;
070    import java.util.LinkedHashMap;
071    import java.util.LinkedHashSet;
072    import java.util.Map;
073    import java.util.Set;
074    
075    import javax.portlet.PortletMode;
076    import javax.portlet.PortletModeException;
077    import javax.portlet.PortletRequest;
078    import javax.portlet.PortletURL;
079    import javax.portlet.ResourceRequest;
080    import javax.portlet.ResourceURL;
081    import javax.portlet.WindowState;
082    import javax.portlet.WindowStateException;
083    
084    import javax.servlet.http.HttpServletRequest;
085    
086    /**
087     * @author Brian Wing Shun Chan
088     * @author Jorge Ferrer
089     * @author Connor McKay
090     */
091    public class PortletURLImpl
092            implements LiferayPortletURL, PortletURL, ResourceURL, Serializable {
093    
094            public PortletURLImpl(
095                    HttpServletRequest request, String portletId, long plid,
096                    String lifecycle) {
097    
098                    _request = request;
099                    _portletId = portletId;
100                    _plid = plid;
101                    _lifecycle = lifecycle;
102                    _parametersIncludedInPath = new LinkedHashSet<String>();
103                    _params = new LinkedHashMap<String, String[]>();
104                    _removePublicRenderParameters = new LinkedHashMap<String, String[]>();
105                    _secure = PortalUtil.isSecure(request);
106                    _wsrp = ParamUtil.getBoolean(request, "wsrp");
107    
108                    Portlet portlet = getPortlet();
109    
110                    if (portlet != null) {
111                            Set<String> autopropagatedParameters =
112                                    portlet.getAutopropagatedParameters();
113    
114                            for (String autopropagatedParameter : autopropagatedParameters) {
115                                    if (PortalUtil.isReservedParameter(autopropagatedParameter)) {
116                                            continue;
117                                    }
118    
119                                    String value = request.getParameter(autopropagatedParameter);
120    
121                                    if (value != null) {
122                                            setParameter(autopropagatedParameter, value);
123                                    }
124                            }
125    
126                            PortletApp portletApp = portlet.getPortletApp();
127    
128                            _escapeXml = MapUtil.getBoolean(
129                                    portletApp.getContainerRuntimeOptions(),
130                                    LiferayPortletConfig.RUNTIME_OPTION_ESCAPE_XML,
131                                    PropsValues.PORTLET_URL_ESCAPE_XML);
132                    }
133    
134                    Layout layout = (Layout)request.getAttribute(WebKeys.LAYOUT);
135    
136                    if ((layout != null) && (layout.getPlid() == _plid) &&
137                            (layout instanceof VirtualLayout)) {
138    
139                            _layout = layout;
140                    }
141            }
142    
143            public PortletURLImpl(
144                    PortletRequest portletRequest, String portletId, long plid,
145                    String lifecycle) {
146    
147                    this(
148                            PortalUtil.getHttpServletRequest(portletRequest), portletId, plid,
149                            lifecycle);
150    
151                    _portletRequest = portletRequest;
152            }
153    
154            @Override
155            public void addParameterIncludedInPath(String name) {
156                    _parametersIncludedInPath.add(name);
157            }
158    
159            @Override
160            public void addProperty(String key, String value) {
161                    if (key == null) {
162                            throw new IllegalArgumentException();
163                    }
164            }
165    
166            @Override
167            public String getCacheability() {
168                    return _cacheability;
169            }
170    
171            public HttpServletRequest getHttpServletRequest() {
172                    return _request;
173            }
174    
175            public Layout getLayout() {
176                    if (_layout == null) {
177                            try {
178                                    if (_plid > 0) {
179                                            _layout = LayoutLocalServiceUtil.getLayout(_plid);
180                                    }
181                            }
182                            catch (Exception e) {
183                                    if (_log.isWarnEnabled()) {
184                                            _log.warn("Layout cannot be found for " + _plid);
185                                    }
186                            }
187                    }
188    
189                    return _layout;
190            }
191    
192            public String getLayoutFriendlyURL() {
193                    return _layoutFriendlyURL;
194            }
195    
196            @Override
197            public String getLifecycle() {
198                    return _lifecycle;
199            }
200    
201            public String getNamespace() {
202                    if (_namespace == null) {
203                            _namespace = PortalUtil.getPortletNamespace(_portletId);
204                    }
205    
206                    return _namespace;
207            }
208    
209            @Override
210            public String getParameter(String name) {
211                    String[] values = _params.get(name);
212    
213                    if ((values != null) && (values.length > 0)) {
214                            return values[0];
215                    }
216                    else {
217                            return null;
218                    }
219            }
220    
221            @Override
222            public Map<String, String[]> getParameterMap() {
223                    return _params;
224            }
225    
226            @Override
227            public Set<String> getParametersIncludedInPath() {
228                    return _parametersIncludedInPath;
229            }
230    
231            public long getPlid() {
232                    return _plid;
233            }
234    
235            public Portlet getPortlet() {
236                    if (_portlet == null) {
237                            try {
238                                    _portlet = PortletLocalServiceUtil.getPortletById(
239                                            PortalUtil.getCompanyId(_request), _portletId);
240                            }
241                            catch (SystemException se) {
242                                    _log.error(se.getMessage());
243                            }
244                    }
245    
246                    return _portlet;
247            }
248    
249            public String getPortletFriendlyURLPath() {
250                    String portletFriendlyURLPath = null;
251    
252                    Portlet portlet = getPortlet();
253    
254                    if (portlet != null) {
255                            FriendlyURLMapper mapper = portlet.getFriendlyURLMapperInstance();
256    
257                            if (mapper != null) {
258                                    portletFriendlyURLPath = mapper.buildPath(this);
259    
260                                    if (_log.isDebugEnabled()) {
261                                            _log.debug(
262                                                    "Portlet friendly URL path " + portletFriendlyURLPath);
263                                    }
264                            }
265                    }
266    
267                    return portletFriendlyURLPath;
268            }
269    
270            @Override
271            public String getPortletId() {
272                    return _portletId;
273            }
274    
275            @Override
276            public PortletMode getPortletMode() {
277                    if (_portletModeString == null) {
278                            return null;
279                    }
280    
281                    return PortletModeFactory.getPortletMode(_portletModeString);
282            }
283    
284            public PortletRequest getPortletRequest() {
285                    return _portletRequest;
286            }
287    
288            @Override
289            public Set<String> getRemovedParameterNames() {
290                    return _removedParameterNames;
291            }
292    
293            @Override
294            public Map<String, String> getReservedParameterMap() {
295                    if (_reservedParameters != null) {
296                            return _reservedParameters;
297                    }
298    
299                    _reservedParameters = new LinkedHashMap<String, String>();
300    
301                    _reservedParameters.put("p_p_id", _portletId);
302    
303                    if (_lifecycle.equals(PortletRequest.ACTION_PHASE)) {
304                            _reservedParameters.put("p_p_lifecycle", "1");
305                    }
306                    else if (_lifecycle.equals(PortletRequest.RENDER_PHASE)) {
307                            _reservedParameters.put("p_p_lifecycle", "0");
308                    }
309                    else if (_lifecycle.equals(PortletRequest.RESOURCE_PHASE)) {
310                            _reservedParameters.put("p_p_lifecycle", "2");
311                    }
312    
313                    if (_windowStateString != null) {
314                            _reservedParameters.put("p_p_state", _windowStateString);
315                    }
316    
317                    if (_windowStateRestoreCurrentView) {
318                            _reservedParameters.put("p_p_state_rcv", "1");
319                    }
320    
321                    if (_portletModeString != null) {
322                            _reservedParameters.put("p_p_mode", _portletModeString);
323                    }
324    
325                    if (_resourceID != null) {
326                            _reservedParameters.put("p_p_resource_id", _resourceID);
327                    }
328    
329                    if (_lifecycle.equals(PortletRequest.RESOURCE_PHASE)) {
330                            _reservedParameters.put("p_p_cacheability", _cacheability);
331                    }
332    
333                    ThemeDisplay themeDisplay = (ThemeDisplay)_request.getAttribute(
334                            WebKeys.THEME_DISPLAY);
335    
336                    PortletDisplay portletDisplay = themeDisplay.getPortletDisplay();
337    
338                    if (Validator.isNotNull(portletDisplay.getColumnId())) {
339                            _reservedParameters.put("p_p_col_id", portletDisplay.getColumnId());
340                    }
341    
342                    if (portletDisplay.getColumnPos() > 0) {
343                            _reservedParameters.put(
344                                    "p_p_col_pos", String.valueOf(portletDisplay.getColumnPos()));
345                    }
346    
347                    if (portletDisplay.getColumnCount() > 0) {
348                            _reservedParameters.put(
349                                    "p_p_col_count",
350                                    String.valueOf(portletDisplay.getColumnCount()));
351                    }
352    
353                    _reservedParameters = Collections.unmodifiableMap(_reservedParameters);
354    
355                    return _reservedParameters;
356            }
357    
358            @Override
359            public String getResourceID() {
360                    return _resourceID;
361            }
362    
363            @Override
364            public WindowState getWindowState() {
365                    if (_windowStateString == null) {
366                            return null;
367                    }
368    
369                    return WindowStateFactory.getWindowState(_windowStateString);
370            }
371    
372            @Override
373            public boolean isAnchor() {
374                    return _anchor;
375            }
376    
377            @Override
378            public boolean isCopyCurrentRenderParameters() {
379                    return _copyCurrentRenderParameters;
380            }
381    
382            @Override
383            public boolean isEncrypt() {
384                    return _encrypt;
385            }
386    
387            @Override
388            public boolean isEscapeXml() {
389                    return _escapeXml;
390            }
391    
392            @Override
393            public boolean isParameterIncludedInPath(String name) {
394                    if (_parametersIncludedInPath.contains(name)) {
395                            return true;
396                    }
397                    else {
398                            return false;
399                    }
400            }
401    
402            @Override
403            public boolean isSecure() {
404                    return _secure;
405            }
406    
407            @Override
408            public void removePublicRenderParameter(String name) {
409                    if (name == null) {
410                            throw new IllegalArgumentException();
411                    }
412    
413                    PublicRenderParameter publicRenderParameter =
414                            _portlet.getPublicRenderParameter(name);
415    
416                    if (publicRenderParameter == null) {
417                            if (_log.isWarnEnabled()) {
418                                    _log.warn("Public parameter " + name + "does not exist");
419                            }
420    
421                            return;
422                    }
423    
424                    QName qName = publicRenderParameter.getQName();
425    
426                    _removePublicRenderParameters.put(
427                            PortletQNameUtil.getRemovePublicRenderParameterName(qName),
428                            new String[] {"1"});
429            }
430    
431            @Override
432            public void setAnchor(boolean anchor) {
433                    _anchor = anchor;
434    
435                    clearCache();
436            }
437    
438            @Override
439            public void setCacheability(String cacheability) {
440                    if (cacheability == null) {
441                            throw new IllegalArgumentException("Cacheability is null");
442                    }
443    
444                    if (!cacheability.equals(FULL) && !cacheability.equals(PORTLET) &&
445                            !cacheability.equals(PAGE)) {
446    
447                            throw new IllegalArgumentException(
448                                    "Cacheability " + cacheability + " is not " + FULL + ", " +
449                                            PORTLET + ", or " + PAGE);
450                    }
451    
452                    if (_portletRequest instanceof ResourceRequest) {
453                            ResourceRequest resourceRequest = (ResourceRequest)_portletRequest;
454    
455                            String parentCacheability = resourceRequest.getCacheability();
456    
457                            if (parentCacheability.equals(FULL)) {
458                                    if (!cacheability.equals(FULL)) {
459                                            throw new IllegalStateException(
460                                                    "Unable to set a weaker cacheability " + cacheability);
461                                    }
462                            }
463                            else if (parentCacheability.equals(PORTLET)) {
464                                    if (!cacheability.equals(FULL) &&
465                                            !cacheability.equals(PORTLET)) {
466    
467                                            throw new IllegalStateException(
468                                                    "Unable to set a weaker cacheability " + cacheability);
469                                    }
470                            }
471                    }
472    
473                    _cacheability = cacheability;
474    
475                    clearCache();
476            }
477    
478            @Override
479            public void setControlPanelCategory(String controlPanelCategory) {
480                    _controlPanelCategory = controlPanelCategory;
481    
482                    clearCache();
483            }
484    
485            @Override
486            public void setCopyCurrentRenderParameters(
487                    boolean copyCurrentRenderParameters) {
488    
489                    _copyCurrentRenderParameters = copyCurrentRenderParameters;
490            }
491    
492            @Override
493            public void setDoAsGroupId(long doAsGroupId) {
494                    _doAsGroupId = doAsGroupId;
495    
496                    clearCache();
497            }
498    
499            @Override
500            public void setDoAsUserId(long doAsUserId) {
501                    _doAsUserId = doAsUserId;
502    
503                    clearCache();
504            }
505    
506            @Override
507            public void setDoAsUserLanguageId(String doAsUserLanguageId) {
508                    _doAsUserLanguageId = doAsUserLanguageId;
509    
510                    clearCache();
511            }
512    
513            @Override
514            public void setEncrypt(boolean encrypt) {
515                    _encrypt = encrypt;
516    
517                    clearCache();
518            }
519    
520            @Override
521            public void setEscapeXml(boolean escapeXml) {
522                    _escapeXml = escapeXml;
523    
524                    clearCache();
525            }
526    
527            @Override
528            public void setLifecycle(String lifecycle) {
529                    _lifecycle = lifecycle;
530    
531                    clearCache();
532            }
533    
534            @Override
535            public void setParameter(String name, String value) {
536                    setParameter(name, value, PropsValues.PORTLET_URL_APPEND_PARAMETERS);
537            }
538    
539            @Override
540            public void setParameter(String name, String value, boolean append) {
541                    if ((name == null) || (value == null)) {
542                            throw new IllegalArgumentException();
543                    }
544    
545                    setParameter(name, new String[] {value}, append);
546            }
547    
548            @Override
549            public void setParameter(String name, String[] values) {
550                    setParameter(name, values, PropsValues.PORTLET_URL_APPEND_PARAMETERS);
551            }
552    
553            @Override
554            public void setParameter(String name, String[] values, boolean append) {
555                    if ((name == null) || (values == null)) {
556                            throw new IllegalArgumentException();
557                    }
558    
559                    for (String value : values) {
560                            if (value == null) {
561                                    throw new IllegalArgumentException();
562                            }
563                    }
564    
565                    if (!append) {
566                            _params.put(name, values);
567                    }
568                    else {
569                            String[] oldValues = _params.get(name);
570    
571                            if (oldValues == null) {
572                                    _params.put(name, values);
573                            }
574                            else {
575                                    String[] newValues = ArrayUtil.append(oldValues, values);
576    
577                                    _params.put(name, newValues);
578                            }
579                    }
580    
581                    clearCache();
582            }
583    
584            @Override
585            public void setParameters(Map<String, String[]> params) {
586                    if (params == null) {
587                            throw new IllegalArgumentException();
588                    }
589                    else {
590                            Map<String, String[]> newParams =
591                                    new LinkedHashMap<String, String[]>();
592    
593                            for (Map.Entry<String, String[]> entry : params.entrySet()) {
594                                    try {
595                                            String key = entry.getKey();
596                                            String[] value = entry.getValue();
597    
598                                            if (key == null) {
599                                                    throw new IllegalArgumentException();
600                                            }
601                                            else if (value == null) {
602                                                    throw new IllegalArgumentException();
603                                            }
604    
605                                            newParams.put(key, value);
606                                    }
607                                    catch (ClassCastException cce) {
608                                            throw new IllegalArgumentException(cce);
609                                    }
610                            }
611    
612                            _params = newParams;
613                    }
614    
615                    clearCache();
616            }
617    
618            @Override
619            public void setPlid(long plid) {
620                    _plid = plid;
621    
622                    clearCache();
623            }
624    
625            @Override
626            public void setPortletId(String portletId) {
627                    _portletId = portletId;
628    
629                    clearCache();
630            }
631    
632            @Override
633            public void setPortletMode(PortletMode portletMode)
634                    throws PortletModeException {
635    
636                    if (_portletRequest != null) {
637                            if (!getPortlet().hasPortletMode(
638                                            _portletRequest.getResponseContentType(), portletMode)) {
639    
640                                    throw new PortletModeException(
641                                            portletMode.toString(), portletMode);
642                            }
643                    }
644    
645                    _portletModeString = portletMode.toString();
646    
647                    clearCache();
648            }
649    
650            public void setPortletMode(String portletMode) throws PortletModeException {
651                    setPortletMode(PortletModeFactory.getPortletMode(portletMode));
652            }
653    
654            @Override
655            public void setProperty(String key, String value) {
656                    if (key == null) {
657                            throw new IllegalArgumentException();
658                    }
659            }
660    
661            public void setRefererGroupId(long refererGroupId) {
662                    _refererGroupId = refererGroupId;
663    
664                    clearCache();
665            }
666    
667            @Override
668            public void setRefererPlid(long refererPlid) {
669                    _refererPlid = refererPlid;
670    
671                    clearCache();
672            }
673    
674            @Override
675            public void setRemovedParameterNames(Set<String> removedParameterNames) {
676                    _removedParameterNames = removedParameterNames;
677    
678                    clearCache();
679            }
680    
681            @Override
682            public void setResourceID(String resourceID) {
683                    _resourceID = resourceID;
684            }
685    
686            @Override
687            public void setSecure(boolean secure) {
688                    _secure = secure;
689    
690                    clearCache();
691            }
692    
693            public void setWindowState(String windowState) throws WindowStateException {
694                    setWindowState(WindowStateFactory.getWindowState(windowState));
695            }
696    
697            @Override
698            public void setWindowState(WindowState windowState)
699                    throws WindowStateException {
700    
701                    if (_portletRequest != null) {
702                            if (!_portletRequest.isWindowStateAllowed(windowState)) {
703                                    throw new WindowStateException(
704                                            windowState.toString(), windowState);
705                            }
706                    }
707    
708                    if (LiferayWindowState.isWindowStatePreserved(
709                                    getWindowState(), windowState)) {
710    
711                            _windowStateString = windowState.toString();
712                    }
713    
714                    clearCache();
715            }
716    
717            public void setWindowStateRestoreCurrentView(
718                    boolean windowStateRestoreCurrentView) {
719    
720                    _windowStateRestoreCurrentView = windowStateRestoreCurrentView;
721            }
722    
723            @Override
724            public String toString() {
725                    if (_toString != null) {
726                            return _toString;
727                    }
728    
729                    _toString = DoPrivilegedUtil.wrap(new ToStringPrivilegedAction());
730    
731                    return _toString;
732            }
733    
734            @Override
735            public void write(Writer writer) throws IOException {
736                    write(writer, _escapeXml);
737            }
738    
739            @Override
740            public void write(Writer writer, boolean escapeXml) throws IOException {
741                    String toString = toString();
742    
743                    if (escapeXml && !_escapeXml) {
744                            toString = HtmlUtil.escape(toString);
745                    }
746    
747                    writer.write(toString);
748            }
749    
750            protected void addPortalAuthToken(StringBundler sb, Key key) {
751                    if (!PropsValues.AUTH_TOKEN_CHECK_ENABLED ||
752                            !_lifecycle.equals(PortletRequest.ACTION_PHASE)) {
753    
754                            return;
755                    }
756    
757                    Set<String> authTokenIgnorePortlets =
758                            PortalUtil.getAuthTokenIgnorePortlets();
759    
760                    if (authTokenIgnorePortlets.contains(_portletId)) {
761                            return;
762                    }
763    
764                    sb.append("p_auth");
765                    sb.append(StringPool.EQUAL);
766                    sb.append(processValue(key, AuthTokenUtil.getToken(_request)));
767                    sb.append(StringPool.AMPERSAND);
768            }
769    
770            protected void addPortletAuthToken(StringBundler sb, Key key) {
771                    if (!PropsValues.PORTLET_ADD_DEFAULT_RESOURCE_CHECK_ENABLED) {
772                            return;
773                    }
774    
775                    HttpServletRequest request = PortalUtil.getOriginalServletRequest(
776                            _request);
777    
778                    String ppauth = ParamUtil.getString(request, "p_p_auth");
779    
780                    String actualPortletAuthenticationToken = AuthTokenUtil.getToken(
781                            _request, _plid, _portletId);
782    
783                    if (Validator.isNotNull(ppauth) &&
784                            ppauth.equals(actualPortletAuthenticationToken)) {
785    
786                            sb.append("p_p_auth");
787                            sb.append(StringPool.EQUAL);
788                            sb.append(processValue(key, ppauth));
789                            sb.append(StringPool.AMPERSAND);
790    
791                            return;
792                    }
793    
794                    if (!_portlet.isAddDefaultResource()) {
795                            return;
796                    }
797    
798                    Portlet portlet = (Portlet)_request.getAttribute(
799                            WebKeys.RENDER_PORTLET);
800    
801                    if (portlet != null) {
802                            String portletId = portlet.getPortletId();
803    
804                            if (portletId.equals(_portletId) ||
805                                    portletId.equals(PortletKeys.CONTROL_PANEL_MENU)) {
806    
807                                    return;
808                            }
809                    }
810    
811                    Set<String> portletAddDefaultResourceCheckWhiteList =
812                            PortalUtil.getPortletAddDefaultResourceCheckWhitelist();
813    
814                    if (portletAddDefaultResourceCheckWhiteList.contains(_portletId)) {
815                            return;
816                    }
817    
818                    sb.append("p_p_auth");
819                    sb.append(StringPool.EQUAL);
820                    sb.append(processValue(key, actualPortletAuthenticationToken));
821                    sb.append(StringPool.AMPERSAND);
822            }
823    
824            protected void clearCache() {
825                    _reservedParameters = null;
826                    _toString = null;
827            }
828    
829            protected String generateToString() {
830                    StringBundler sb = new StringBundler(64);
831    
832                    ThemeDisplay themeDisplay = (ThemeDisplay)_request.getAttribute(
833                            WebKeys.THEME_DISPLAY);
834    
835                    String portalURL = null;
836    
837                    if (themeDisplay.isFacebook()) {
838                            portalURL =
839                                    FacebookUtil.FACEBOOK_APPS_URL +
840                                            themeDisplay.getFacebookCanvasPageURL();
841                    }
842                    else {
843                            portalURL = PortalUtil.getPortalURL(_request, _secure);
844                    }
845    
846                    try {
847                            if (_layoutFriendlyURL == null) {
848                                    Layout layout = getLayout();
849    
850                                    if (layout != null) {
851                                            _layoutFriendlyURL = GetterUtil.getString(
852                                                    PortalUtil.getLayoutFriendlyURL(layout, themeDisplay));
853    
854                                            if (_secure) {
855                                                    _layoutFriendlyURL = HttpUtil.protocolize(
856                                                            _layoutFriendlyURL, true);
857                                            }
858                                    }
859                            }
860                    }
861                    catch (Exception e) {
862                            _log.error(e);
863                    }
864    
865                    Key key = null;
866    
867                    try {
868                            if (_encrypt) {
869                                    Company company = PortalUtil.getCompany(_request);
870    
871                                    key = company.getKeyObj();
872                            }
873                    }
874                    catch (Exception e) {
875                            _log.error(e);
876                    }
877    
878                    if (Validator.isNull(_layoutFriendlyURL)) {
879                            sb.append(portalURL);
880                            sb.append(themeDisplay.getPathMain());
881                            sb.append("/portal/layout?");
882    
883                            addPortalAuthToken(sb, key);
884    
885                            sb.append("p_l_id");
886                            sb.append(StringPool.EQUAL);
887                            sb.append(processValue(key, _plid));
888                            sb.append(StringPool.AMPERSAND);
889                    }
890                    else {
891                            if (themeDisplay.isFacebook()) {
892                                    sb.append(portalURL);
893                            }
894                            else {
895    
896                                    // A virtual host URL will contain the complete path. Do not
897                                    // append the portal URL if the virtual host URL starts with
898                                    // "http://" or "https://".
899    
900                                    if (!_layoutFriendlyURL.startsWith(Http.HTTP_WITH_SLASH) &&
901                                            !_layoutFriendlyURL.startsWith(Http.HTTPS_WITH_SLASH)) {
902    
903                                            sb.append(portalURL);
904                                    }
905    
906                                    sb.append(_layoutFriendlyURL);
907                            }
908    
909                            String friendlyURLPath = getPortletFriendlyURLPath();
910    
911                            if (Validator.isNotNull(friendlyURLPath)) {
912                                    if (themeDisplay.isFacebook()) {
913                                            int pos = friendlyURLPath.indexOf(CharPool.SLASH, 1);
914    
915                                            if (pos != -1) {
916                                                    sb.append(friendlyURLPath.substring(pos));
917                                            }
918                                            else {
919                                                    sb.append(friendlyURLPath);
920                                            }
921                                    }
922                                    else {
923                                            sb.append("/-");
924                                            sb.append(friendlyURLPath);
925                                    }
926                            }
927    
928                            sb.append(StringPool.QUESTION);
929    
930                            addPortalAuthToken(sb, key);
931                    }
932    
933                    addPortletAuthToken(sb, key);
934    
935                    for (Map.Entry<String, String> entry :
936                                    getReservedParameterMap().entrySet()) {
937    
938                            String name = entry.getKey();
939    
940                            if (!isParameterIncludedInPath(name)) {
941                                    sb.append(HttpUtil.encodeURL(name));
942                                    sb.append(StringPool.EQUAL);
943                                    sb.append(processValue(key, entry.getValue()));
944                                    sb.append(StringPool.AMPERSAND);
945                            }
946                    }
947    
948                    if (_doAsUserId > 0) {
949                            try {
950                                    Company company = PortalUtil.getCompany(_request);
951    
952                                    sb.append("doAsUserId");
953                                    sb.append(StringPool.EQUAL);
954                                    sb.append(processValue(company.getKeyObj(), _doAsUserId));
955                                    sb.append(StringPool.AMPERSAND);
956                            }
957                            catch (Exception e) {
958                                    _log.error(e);
959                            }
960                    }
961                    else {
962                            String doAsUserId = themeDisplay.getDoAsUserId();
963    
964                            if (Validator.isNotNull(doAsUserId)) {
965                                    sb.append("doAsUserId");
966                                    sb.append(StringPool.EQUAL);
967                                    sb.append(processValue(key, doAsUserId));
968                                    sb.append(StringPool.AMPERSAND);
969                            }
970                    }
971    
972                    String doAsUserLanguageId = _doAsUserLanguageId;
973    
974                    if (Validator.isNull(doAsUserLanguageId)) {
975                            doAsUserLanguageId = themeDisplay.getDoAsUserLanguageId();
976                    }
977    
978                    if (Validator.isNotNull(doAsUserLanguageId)) {
979                            sb.append("doAsUserLanguageId");
980                            sb.append(StringPool.EQUAL);
981                            sb.append(processValue(key, doAsUserLanguageId));
982                            sb.append(StringPool.AMPERSAND);
983                    }
984    
985                    long doAsGroupId = _doAsGroupId;
986    
987                    if (doAsGroupId <= 0) {
988                            doAsGroupId = themeDisplay.getDoAsGroupId();
989                    }
990    
991                    if (doAsGroupId > 0) {
992                            sb.append("doAsGroupId");
993                            sb.append(StringPool.EQUAL);
994                            sb.append(processValue(key, doAsGroupId));
995                            sb.append(StringPool.AMPERSAND);
996                    }
997    
998                    long refererGroupId = _refererGroupId;
999    
1000                    if (refererGroupId <= 0) {
1001                            refererGroupId = themeDisplay.getRefererGroupId();
1002                    }
1003    
1004                    if (refererGroupId > 0) {
1005                            sb.append("refererGroupId");
1006                            sb.append(StringPool.EQUAL);
1007                            sb.append(processValue(key, refererGroupId));
1008                            sb.append(StringPool.AMPERSAND);
1009                    }
1010    
1011                    long refererPlid = _refererPlid;
1012    
1013                    if (refererPlid <= 0) {
1014                            refererPlid = themeDisplay.getRefererPlid();
1015                    }
1016    
1017                    if (refererPlid > 0) {
1018                            sb.append("refererPlid");
1019                            sb.append(StringPool.EQUAL);
1020                            sb.append(processValue(key, refererPlid));
1021                            sb.append(StringPool.AMPERSAND);
1022                    }
1023    
1024                    String controlPanelCategory = _controlPanelCategory;
1025    
1026                    if (Validator.isNull(controlPanelCategory)) {
1027                            controlPanelCategory = themeDisplay.getControlPanelCategory();
1028                    }
1029    
1030                    if (Validator.isNotNull(controlPanelCategory)) {
1031                            sb.append("controlPanelCategory");
1032                            sb.append(StringPool.EQUAL);
1033                            sb.append(processValue(key, controlPanelCategory));
1034                            sb.append(StringPool.AMPERSAND);
1035                    }
1036    
1037                    Iterator<Map.Entry<String, String[]>> itr =
1038                            _removePublicRenderParameters.entrySet().iterator();
1039    
1040                    while (itr.hasNext()) {
1041                            String lastString = sb.stringAt(sb.index() - 1);
1042    
1043                            if (lastString.charAt(lastString.length() - 1) !=
1044                                            CharPool.AMPERSAND) {
1045    
1046                                    sb.append(StringPool.AMPERSAND);
1047                            }
1048    
1049                            Map.Entry<String, String[]> entry = itr.next();
1050    
1051                            sb.append(HttpUtil.encodeURL(entry.getKey()));
1052                            sb.append(StringPool.EQUAL);
1053                            sb.append(processValue(key, entry.getValue()[0]));
1054                            sb.append(StringPool.AMPERSAND);
1055                    }
1056    
1057                    if (_copyCurrentRenderParameters) {
1058                            mergeRenderParameters();
1059                    }
1060    
1061                    itr = _params.entrySet().iterator();
1062    
1063                    while (itr.hasNext()) {
1064                            Map.Entry<String, String[]> entry = itr.next();
1065    
1066                            String name = entry.getKey();
1067                            String[] values = entry.getValue();
1068    
1069                            if (isParameterIncludedInPath(name)) {
1070                                    continue;
1071                            }
1072    
1073                            String publicRenderParameterName = getPublicRenderParameterName(
1074                                    name);
1075    
1076                            if (Validator.isNotNull(publicRenderParameterName)) {
1077                                    name = publicRenderParameterName;
1078                            }
1079    
1080                            name = HttpUtil.encodeURL(prependNamespace(name));
1081    
1082                            for (int i = 0; i < values.length; i++) {
1083                                    sb.append(name);
1084                                    sb.append(StringPool.EQUAL);
1085                                    sb.append(processValue(key, values[i]));
1086    
1087                                    if (((i + 1) < values.length) || itr.hasNext()) {
1088                                            sb.append(StringPool.AMPERSAND);
1089                                    }
1090                            }
1091                    }
1092    
1093                    if (_encrypt) {
1094                            sb.append(StringPool.AMPERSAND + WebKeys.ENCRYPT + "=1");
1095                    }
1096    
1097                    if (PropsValues.PORTLET_URL_ANCHOR_ENABLE) {
1098                            if (_anchor && (_windowStateString != null) &&
1099                                    !_windowStateString.equals(WindowState.MAXIMIZED.toString()) &&
1100                                    !_windowStateString.equals(
1101                                            LiferayWindowState.EXCLUSIVE.toString()) &&
1102                                    !_windowStateString.equals(
1103                                            LiferayWindowState.POP_UP.toString())) {
1104    
1105                                    String lastString = sb.stringAt(sb.index() - 1);
1106    
1107                                    if (lastString.charAt(lastString.length() - 1) !=
1108                                                    CharPool.AMPERSAND) {
1109    
1110                                            sb.append(StringPool.AMPERSAND);
1111                                    }
1112    
1113                                    sb.append("#p_");
1114                                    sb.append(HttpUtil.encodeURL(_portletId));
1115                            }
1116                    }
1117    
1118                    String result = sb.toString();
1119    
1120                    if (result.endsWith(StringPool.AMPERSAND) ||
1121                            result.endsWith(StringPool.QUESTION)) {
1122    
1123                            result = result.substring(0, result.length() - 1);
1124                    }
1125    
1126                    if (themeDisplay.isFacebook()) {
1127    
1128                            // Facebook requires the path portion of the URL to end with a slash
1129    
1130                            int pos = result.indexOf(CharPool.QUESTION);
1131    
1132                            if (pos == -1) {
1133                                    if (!result.endsWith(StringPool.SLASH)) {
1134                                            result += StringPool.SLASH;
1135                                    }
1136                            }
1137                            else {
1138                                    String path = result.substring(0, pos);
1139    
1140                                    if (!result.endsWith(StringPool.SLASH)) {
1141                                            result = path + StringPool.SLASH + result.substring(pos);
1142                                    }
1143                            }
1144                    }
1145                    else if (!CookieKeys.hasSessionId(_request)) {
1146                            result = PortalUtil.getURLWithSessionId(
1147                                    result, _request.getSession().getId());
1148                    }
1149    
1150                    if (_escapeXml) {
1151                            result = HtmlUtil.escape(result);
1152                    }
1153    
1154                    if (result.length() > _URL_MAXIMUM_LENGTH) {
1155                            result = shortenURL(result, 2);
1156                    }
1157    
1158                    return result;
1159            }
1160    
1161            protected String generateWSRPToString() {
1162                    StringBundler sb = new StringBundler("wsrp_rewrite?");
1163    
1164                    sb.append("wsrp-urlType");
1165                    sb.append(StringPool.EQUAL);
1166    
1167                    if (_lifecycle.equals(PortletRequest.ACTION_PHASE)) {
1168                            sb.append(HttpUtil.encodeURL("blockingAction"));
1169                    }
1170                    else if (_lifecycle.equals(PortletRequest.RENDER_PHASE)) {
1171                            sb.append(HttpUtil.encodeURL("render"));
1172                    }
1173                    else if (_lifecycle.equals(PortletRequest.RESOURCE_PHASE)) {
1174                            sb.append(HttpUtil.encodeURL("resource"));
1175                    }
1176    
1177                    sb.append(StringPool.AMPERSAND);
1178    
1179                    if (_windowStateString != null) {
1180                            sb.append("wsrp-windowState");
1181                            sb.append(StringPool.EQUAL);
1182                            sb.append(HttpUtil.encodeURL("wsrp:" + _windowStateString));
1183                            sb.append(StringPool.AMPERSAND);
1184                    }
1185    
1186                    if (_portletModeString != null) {
1187                            sb.append("wsrp-mode");
1188                            sb.append(StringPool.EQUAL);
1189                            sb.append(HttpUtil.encodeURL("wsrp:" + _portletModeString));
1190                            sb.append(StringPool.AMPERSAND);
1191                    }
1192    
1193                    if (_resourceID != null) {
1194                            sb.append("wsrp-resourceID");
1195                            sb.append(StringPool.EQUAL);
1196                            sb.append(HttpUtil.encodeURL(_resourceID));
1197                            sb.append(StringPool.AMPERSAND);
1198                    }
1199    
1200                    if (_lifecycle.equals(PortletRequest.RESOURCE_PHASE)) {
1201                            sb.append("wsrp-resourceCacheability");
1202                            sb.append(StringPool.EQUAL);
1203                            sb.append(HttpUtil.encodeURL(_cacheability));
1204                            sb.append(StringPool.AMPERSAND);
1205                    }
1206    
1207                    if (PropsValues.PORTLET_URL_ANCHOR_ENABLE) {
1208                            if (_anchor && (_windowStateString != null) &&
1209                                    !_windowStateString.equals(WindowState.MAXIMIZED.toString()) &&
1210                                    !_windowStateString.equals(
1211                                            LiferayWindowState.EXCLUSIVE.toString()) &&
1212                                    !_windowStateString.equals(
1213                                            LiferayWindowState.POP_UP.toString())) {
1214    
1215                                    sb.append("wsrp-fragmentID");
1216                                    sb.append(StringPool.EQUAL);
1217                                    sb.append("#p_");
1218                                    sb.append(HttpUtil.encodeURL(_portletId));
1219                                    sb.append(StringPool.AMPERSAND);
1220                            }
1221                    }
1222    
1223                    if (_copyCurrentRenderParameters) {
1224                            mergeRenderParameters();
1225                    }
1226    
1227                    StringBundler parameterSb = new StringBundler();
1228    
1229                    Iterator<Map.Entry<String, String[]>> itr =
1230                            _params.entrySet().iterator();
1231    
1232                    while (itr.hasNext()) {
1233                            Map.Entry<String, String[]> entry = itr.next();
1234    
1235                            String name = entry.getKey();
1236                            String[] values = entry.getValue();
1237    
1238                            if (isParameterIncludedInPath(name)) {
1239                                    continue;
1240                            }
1241    
1242                            String publicRenderParameterName = getPublicRenderParameterName(
1243                                    name);
1244    
1245                            if (Validator.isNotNull(publicRenderParameterName)) {
1246                                    name = publicRenderParameterName;
1247                            }
1248    
1249                            name = HttpUtil.encodeURL(prependNamespace(name));
1250    
1251                            for (int i = 0; i < values.length; i++) {
1252                                    parameterSb.append(name);
1253                                    parameterSb.append(StringPool.EQUAL);
1254                                    parameterSb.append(HttpUtil.encodeURL(values[i]));
1255    
1256                                    if (((i + 1) < values.length) || itr.hasNext()) {
1257                                            parameterSb.append(StringPool.AMPERSAND);
1258                                    }
1259                            }
1260                    }
1261    
1262                    sb.append("wsrp-navigationalState");
1263                    sb.append(StringPool.EQUAL);
1264    
1265                    byte[] parameterBytes = null;
1266    
1267                    try {
1268                            String parameterString = parameterSb.toString();
1269    
1270                            parameterBytes = parameterString.getBytes(StringPool.UTF8);
1271                    }
1272                    catch (UnsupportedEncodingException uee) {
1273                            if (_log.isWarnEnabled()) {
1274                                    _log.warn(uee, uee);
1275                            }
1276                    }
1277    
1278                    String navigationalState = Base64.toURLSafe(
1279                            Base64.encode(parameterBytes));
1280    
1281                    sb.append(navigationalState);
1282    
1283                    sb.append("/wsrp_rewrite");
1284    
1285                    return sb.toString();
1286            }
1287    
1288            protected String getPublicRenderParameterName(String name) {
1289                    Portlet portlet = getPortlet();
1290    
1291                    String publicRenderParameterName = null;
1292    
1293                    if (portlet != null) {
1294                            PublicRenderParameter publicRenderParameter =
1295                                    portlet.getPublicRenderParameter(name);
1296    
1297                            if (publicRenderParameter != null) {
1298                                    QName qName = publicRenderParameter.getQName();
1299    
1300                                    publicRenderParameterName =
1301                                            PortletQNameUtil.getPublicRenderParameterName(qName);
1302                            }
1303                    }
1304    
1305                    return publicRenderParameterName;
1306            }
1307    
1308            protected boolean isBlankValue(String[] value) {
1309                    if ((value != null) && (value.length == 1) &&
1310                            value[0].equals(StringPool.BLANK)) {
1311    
1312                            return true;
1313                    }
1314                    else {
1315                            return false;
1316                    }
1317            }
1318    
1319            protected void mergeRenderParameters() {
1320                    String namespace = getNamespace();
1321    
1322                    Layout layout = getLayout();
1323    
1324                    Map<String, String[]> renderParameters = RenderParametersPool.get(
1325                            _request, layout.getPlid(), getPortlet().getPortletId());
1326    
1327                    for (Map.Entry<String, String[]> entry : renderParameters.entrySet()) {
1328                            String name = entry.getKey();
1329    
1330                            if (name.contains(namespace)) {
1331                                    name = name.substring(namespace.length());
1332                            }
1333    
1334                            if (!_lifecycle.equals(PortletRequest.RESOURCE_PHASE) &&
1335                                    (_removedParameterNames != null) &&
1336                                    _removedParameterNames.contains(name)) {
1337    
1338                                    continue;
1339                            }
1340    
1341                            String[] oldValues = entry.getValue();
1342                            String[] newValues = _params.get(name);
1343    
1344                            if (newValues == null) {
1345                                    _params.put(name, oldValues);
1346                            }
1347                            else if (isBlankValue(newValues)) {
1348                                    _params.remove(name);
1349                            }
1350                            else {
1351                                    newValues = ArrayUtil.append(newValues, oldValues);
1352    
1353                                    _params.put(name, newValues);
1354                            }
1355                    }
1356            }
1357    
1358            protected String prependNamespace(String name) {
1359                    String namespace = getNamespace();
1360    
1361                    if (!PortalUtil.isReservedParameter(name) &&
1362                            !name.startsWith(PortletQName.PUBLIC_RENDER_PARAMETER_NAMESPACE) &&
1363                            !name.startsWith(namespace)) {
1364    
1365                            return namespace.concat(name);
1366                    }
1367                    else {
1368                            return name;
1369                    }
1370            }
1371    
1372            protected String processValue(Key key, int value) {
1373                    return processValue(key, String.valueOf(value));
1374            }
1375    
1376            protected String processValue(Key key, long value) {
1377                    return processValue(key, String.valueOf(value));
1378            }
1379    
1380            protected String processValue(Key key, String value) {
1381                    if (key == null) {
1382                            return HttpUtil.encodeURL(value);
1383                    }
1384                    else {
1385                            try {
1386                                    return HttpUtil.encodeURL(Encryptor.encrypt(key, value));
1387                            }
1388                            catch (EncryptorException ee) {
1389                                    return value;
1390                            }
1391                    }
1392            }
1393    
1394            protected String shortenURL(String url, int count) {
1395                    if (count == 0) {
1396                            return null;
1397                    }
1398    
1399                    StringBundler sb = new StringBundler();
1400    
1401                    String[] params = url.split(StringPool.AMPERSAND);
1402    
1403                    for (int i = 0; i < params.length; i++) {
1404                            String param = params[i];
1405    
1406                            if (param.contains("_backURL=") || param.contains("_redirect=") ||
1407                                    param.contains("_returnToFullPageURL=")) {
1408    
1409                                    int pos = param.indexOf(StringPool.EQUAL);
1410    
1411                                    String qName = param.substring(0, pos);
1412    
1413                                    String redirect = param.substring(pos + 1);
1414    
1415                                    redirect = HttpUtil.decodeURL(redirect);
1416    
1417                                    String newURL = shortenURL(redirect, count - 1);
1418    
1419                                    if (newURL != null) {
1420                                            newURL = HttpUtil.encodeURL(newURL);
1421    
1422                                            sb.append(qName);
1423                                            sb.append(StringPool.EQUAL);
1424                                            sb.append(newURL);
1425    
1426                                            if (i < (params.length - 1)) {
1427                                                    sb.append(StringPool.AMPERSAND);
1428                                            }
1429                                    }
1430                            }
1431                            else {
1432                                    sb.append(param);
1433    
1434                                    if (i < (params.length - 1)) {
1435                                            sb.append(StringPool.AMPERSAND);
1436                                    }
1437                            }
1438                    }
1439    
1440                    return sb.toString();
1441            }
1442    
1443            private static final long _URL_MAXIMUM_LENGTH = 2083;
1444    
1445            private static Log _log = LogFactoryUtil.getLog(PortletURLImpl.class);
1446    
1447            private boolean _anchor = true;
1448            private String _cacheability = ResourceURL.PAGE;
1449            private String _controlPanelCategory;
1450            private boolean _copyCurrentRenderParameters;
1451            private long _doAsGroupId;
1452            private long _doAsUserId;
1453            private String _doAsUserLanguageId;
1454            private boolean _encrypt;
1455            private boolean _escapeXml = PropsValues.PORTLET_URL_ESCAPE_XML;
1456            private Layout _layout;
1457            private String _layoutFriendlyURL;
1458            private String _lifecycle;
1459            private String _namespace;
1460            private Set<String> _parametersIncludedInPath;
1461            private Map<String, String[]> _params;
1462            private long _plid;
1463            private Portlet _portlet;
1464            private String _portletId;
1465            private String _portletModeString;
1466            private PortletRequest _portletRequest;
1467            private long _refererGroupId;
1468            private long _refererPlid;
1469            private Set<String> _removedParameterNames;
1470            private Map<String, String[]> _removePublicRenderParameters;
1471            private HttpServletRequest _request;
1472            private Map<String, String> _reservedParameters;
1473            private String _resourceID;
1474            private boolean _secure;
1475            private String _toString;
1476            private boolean _windowStateRestoreCurrentView;
1477            private String _windowStateString;
1478            private boolean _wsrp;
1479    
1480            private class ToStringPrivilegedAction implements PrivilegedAction<String> {
1481    
1482                    @Override
1483                    public String run() {
1484                            if (_wsrp) {
1485                                    return generateWSRPToString();
1486                            }
1487    
1488                            return generateToString();
1489                    }
1490            }
1491    
1492    }