001 package railo.runtime.util; 002 003 import java.net.MalformedURLException; 004 import java.net.URL; 005 006 import org.w3c.dom.Element; 007 import org.w3c.dom.NamedNodeMap; 008 import org.w3c.dom.Node; 009 import org.w3c.dom.NodeList; 010 011 import railo.commons.lang.StringUtil; 012 import railo.runtime.exp.PageException; 013 import railo.runtime.op.Caster; 014 import railo.runtime.text.xml.XMLUtil; 015 import railo.transformer.util.CFMLString; 016 017 /** 018 * Transform a HTML String, set all relative Pathes inside HTML File to absolute 019 * TODO Test this 020 * 021 */ 022 public final class URLResolver { 023 024 private Tag[] tags=new Tag[]{ 025 new Tag("a","href"), 026 new Tag("link","href"), 027 new Tag("form","action"), 028 new Tag("applet","code"), 029 new Tag("script","src"), 030 new Tag("body","background"), 031 new Tag("frame","src"), 032 new Tag("bgsound","src"), 033 new Tag("img","src"), 034 035 new Tag("embed",new String[]{"src","pluginspace"}), 036 new Tag("object",new String[]{"data","classid","codebase","usemap"}) 037 038 }; 039 040 041 public void transform(Node node, URL url) throws MalformedURLException { 042 Element el; 043 if(node.getNodeType()==Node.DOCUMENT_NODE) { 044 transform(XMLUtil.getRootElement(node, true), url); 045 } 046 else if(node.getNodeType()==Node.ELEMENT_NODE) { 047 el=(Element) node; 048 String[] attr; 049 NamedNodeMap map; 050 String attrName,value,value2,nodeName=el.getNodeName(); 051 int len; 052 // translate attribute 053 for(int i=0;i<tags.length;i++) { 054 if(tags[i].tag.equalsIgnoreCase(nodeName)) { 055 056 attr=tags[i].attributes; 057 map = el.getAttributes(); 058 len = map.getLength(); 059 for(int y=0;y<attr.length;y++) { 060 for(int z=0;z<len;z++) { 061 attrName=map.item(z).getNodeName(); 062 if(attrName.equalsIgnoreCase(attr[y])) { 063 value=el.getAttribute(attrName); 064 value2=add(url, value); 065 066 if(value!=value2){ 067 el.setAttribute(attrName, value2); 068 } 069 070 break; 071 } 072 } 073 } 074 } 075 } 076 077 // list children 078 NodeList nodes = el.getChildNodes(); 079 len=nodes.getLength(); 080 for(int i=0;i<len;i++) { 081 transform(nodes.item(i), url); 082 } 083 } 084 } 085 086 /** 087 * transform the HTML String 088 * @param html HTML String to transform 089 * @param url Absolute URL path to set 090 * @return transformed HTMl String 091 * @throws PageException 092 */ 093 public String transform(String html, URL url, boolean setBaseTag) throws PageException { 094 StringBuffer target=new StringBuffer(); 095 CFMLString cfml=new CFMLString(html,"UTF-8"); 096 while(!cfml.isAfterLast()) { 097 if(cfml.forwardIfCurrent('<')) { 098 target.append('<'); 099 try { 100 for(int i=0;i<tags.length;i++) { 101 if(cfml.forwardIfCurrent(tags[i].tag+" ")) { 102 target.append(tags[i].tag+" "); 103 transformTag(target,cfml,tags[i],url); 104 } 105 } 106 } 107 catch(MalformedURLException me) { 108 throw Caster.toPageException(me); 109 } 110 } 111 else { 112 target.append(cfml.getCurrent()); 113 cfml.next(); 114 } 115 116 } 117 if(!setBaseTag)return target.toString(); 118 119 html=target.toString(); 120 String prefix="",postfix=""; 121 int index = StringUtil.indexOfIgnoreCase(html, "</head>"); 122 if(index==-1) { 123 prefix="<head>"; 124 postfix="</head>"; 125 index = StringUtil.indexOfIgnoreCase(html, "</html>"); 126 } 127 128 129 if(index!=-1) { 130 StringBuffer sb=new StringBuffer(); 131 sb.append(html.substring(0,index)); 132 String port=url.getPort()==-1?"":":"+url.getPort(); 133 sb.append(prefix+"<base href=\""+(url.getProtocol()+"://"+url.getHost()+port)+"\">"+postfix); 134 sb.append(html.substring(index)); 135 html=sb.toString(); 136 137 } 138 return html; 139 } 140 141 /** 142 * transform a single tag 143 * @param target target to write to 144 * @param cfml CFMl String Object containing plain HTML 145 * @param tag current tag totransform 146 * @param url absolute URL to Set at tag attribute 147 * @throws MalformedURLException 148 */ 149 private void transformTag(StringBuffer target, CFMLString cfml, Tag tag,URL url) throws MalformedURLException { 150 // TODO attribute inside other attribute 151 152 char quote=0; 153 boolean inside=false; 154 StringBuffer value=new StringBuffer(); 155 156 while(!cfml.isAfterLast()) { 157 if(inside) { 158 if(quote!=0 && cfml.forwardIfCurrent(quote)) { 159 inside=false; 160 target.append(add(url,value.toString())); 161 target.append(quote); 162 } 163 else if(quote==0 && (cfml.isCurrent(' ')||cfml.isCurrent("/>")||cfml.isCurrent('>')||cfml.isCurrent('\t')||cfml.isCurrent('\n'))) { 164 165 inside=false; 166 target.append(new URL(url,value.toString())); 167 target.append(cfml.getCurrent()); 168 cfml.next(); 169 } 170 else { 171 value.append(cfml.getCurrent()); 172 cfml.next(); 173 } 174 } 175 else if(cfml.forwardIfCurrent('>')) { 176 target.append('>'); 177 break; 178 } 179 else { 180 181 for(int i=0;i<tag.attributes.length;i++) { 182 if(cfml.forwardIfCurrent(tag.attributes[i])) { 183 target.append(tag.attributes[i]); 184 cfml.removeSpace(); 185 // = 186 if(cfml.isCurrent('=')) { 187 inside=true; 188 target.append('='); 189 cfml.next(); 190 cfml.removeSpace(); 191 192 quote=cfml.getCurrent(); 193 value=new StringBuffer(); 194 if(quote!='"' && quote!='\'')quote=0; 195 else { 196 target.append(quote); 197 cfml.next(); 198 } 199 } 200 } 201 } 202 if(!inside) { 203 target.append(cfml.getCurrent()); 204 cfml.next(); 205 } 206 } 207 } 208 } 209 210 211 private String add(URL url, String value) { 212 value=value.trim(); 213 String lcValue=value.toLowerCase(); 214 if(lcValue.startsWith("http://") || lcValue.startsWith("file://") || lcValue.startsWith("news://") || lcValue.startsWith("goopher://") || lcValue.startsWith("javascript:")) 215 return (value); 216 try { 217 return new URL(url,value.toString()).toExternalForm(); 218 } catch (MalformedURLException e) { 219 return value; 220 } 221 } 222 223 private class Tag { 224 private String tag; 225 private String[] attributes; 226 private Tag(String tag,String[] attributes) { 227 this.tag=tag.toLowerCase(); 228 this.attributes=new String[attributes.length]; 229 for(int i=0;i<attributes.length;i++) { 230 this.attributes[i]=attributes[i].toLowerCase(); 231 } 232 233 } 234 private Tag(String tag,String attribute1) { 235 this.tag=tag.toLowerCase(); 236 this.attributes=new String[]{attribute1.toLowerCase()}; 237 } 238 239 } 240 241 public static URLResolver getInstance() { 242 return new URLResolver(); 243 } 244 245 }