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    }