/* This file is part of Airsonic. Airsonic is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Airsonic is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Airsonic. If not, see . Copyright 2016 (C) Airsonic Authors Based upon Subsonic, Copyright 2009 (C) Sindre Mehus */ package org.airsonic.player.taglib; import org.airsonic.player.filter.ParameterDecodingFilter; import org.airsonic.player.util.StringUtil; import org.apache.commons.lang.CharUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.taglibs.standard.tag.common.core.UrlSupport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspTagException; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.BodyTagSupport; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; /** * Creates a URL with optional query parameters. Similar to 'c:url', but * you may specify which character encoding to use for the URL query * parameters. If no encoding is specified, the following steps are performed: * *

* (The problem with c:url is that is uses the same encoding as the http response, * but most(?) servlet container assumes that ISO-8859-1 is used.) * * @author Sindre Mehus */ public class UrlTag extends BodyTagSupport { private String DEFAULT_ENCODING = "Utf8Hex"; private static final Logger LOG = LoggerFactory.getLogger(UrlTag.class); private String var; private String value; private String encoding = DEFAULT_ENCODING; private List> parameters = new ArrayList<>(); public int doStartTag() { parameters.clear(); return EVAL_BODY_BUFFERED; } public int doEndTag() throws JspException { // Rewrite and encode the url. String result = formatUrl(); // Store or print the output if (var != null) pageContext.setAttribute(var, result, PageContext.PAGE_SCOPE); else { try { pageContext.getOut().print(result); } catch (IOException x) { throw new JspTagException(x); } } return EVAL_PAGE; } private String formatUrl() throws JspException { String baseUrl = UrlSupport.resolveUrl(value, null, pageContext); StringBuilder result = new StringBuilder(); result.append(baseUrl); if (!parameters.isEmpty()) { result.append('?'); for (int i = 0; i < parameters.size(); i++) { Pair parameter = parameters.get(i); try { result.append(parameter.getLeft()); if (isUtf8Hex() && !isAsciiAlphaNumeric(parameter.getRight())) { result.append(ParameterDecodingFilter.PARAM_SUFFIX); } result.append('='); if (parameter.getRight() != null) { result.append(encode(parameter.getRight())); } if (i < parameters.size() - 1) { result.append("&"); } } catch (UnsupportedEncodingException x) { throw new JspTagException(x); } } } return result.toString(); } private String encode(String s) throws UnsupportedEncodingException { if (isUtf8Hex()) { if (isAsciiAlphaNumeric(s)) { return s; } try { return StringUtil.utf8HexEncode(s); } catch (Exception x) { LOG.error("Failed to utf8hex-encode the string '" + s + "'.", x); return s; } } return URLEncoder.encode(s, encoding); } private boolean isUtf8Hex() { return DEFAULT_ENCODING.equals(encoding); } static private boolean isAsciiAlphaNumeric(String s) { if (s == null) { return true; } for (int i = 0; i < s.length(); i++) { if (!CharUtils.isAsciiAlphanumeric(s.charAt(i))) { return false; } } return true; } public void release() { var = null; value = null; encoding = DEFAULT_ENCODING; parameters.clear(); super.release(); } public void addParameter(String name, String value) { parameters.add(Pair.of(name, value)); } public String getVar() { return var; } public void setVar(String var) { this.var = var; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public String getEncoding() { return encoding; } public void setEncoding(String encoding) { this.encoding = encoding; } }