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