001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.transformer.cfml.evaluator.impl;
020
021import java.io.ByteArrayInputStream;
022import java.io.ByteArrayOutputStream;
023import java.io.IOException;
024import java.util.zip.ZipEntry;
025import java.util.zip.ZipInputStream;
026
027import lucee.commons.io.IOUtil;
028import lucee.commons.io.res.Resource;
029import lucee.commons.io.res.util.ResourceUtil;
030import lucee.commons.lang.Md5;
031import lucee.commons.lang.StringUtil;
032import lucee.commons.lang.SystemOut;
033import lucee.loader.util.Util;
034import lucee.runtime.PageSource;
035import lucee.runtime.config.Config;
036import lucee.runtime.config.ConfigImpl;
037import lucee.runtime.config.ConfigWebUtil;
038import lucee.runtime.exp.TemplateException;
039import lucee.transformer.bytecode.statement.tag.Attribute;
040import lucee.transformer.bytecode.statement.tag.Tag;
041import lucee.transformer.bytecode.statement.tag.TagImport;
042import lucee.transformer.bytecode.util.ASMUtil;
043import lucee.transformer.cfml.Data;
044import lucee.transformer.cfml.evaluator.EvaluatorException;
045import lucee.transformer.cfml.evaluator.EvaluatorSupport;
046import lucee.transformer.library.function.FunctionLib;
047import lucee.transformer.library.tag.CustomTagLib;
048import lucee.transformer.library.tag.TagLib;
049import lucee.transformer.library.tag.TagLibException;
050import lucee.transformer.library.tag.TagLibFactory;
051import lucee.transformer.library.tag.TagLibTag;
052import lucee.transformer.util.CFMLString;
053
054
055/**
056 * 
057 */
058public final class Import extends EvaluatorSupport {
059        
060        public void evaluate(Tag tag,TagLibTag libTag) throws EvaluatorException {
061        }
062
063    /**
064     * @see lucee.transformer.cfml.evaluator.Evaluator#execute(lucee.runtime.config.Config, org.w3c.dom.Element, lucee.transformer.library.tag.TagLibTag, lucee.transformer.library.function.FunctionLib[], lucee.transformer.util.CFMLString)
065     */
066        public TagLib execute(Config config,Tag tag, TagLibTag libTag, FunctionLib[] flibs,Data data) throws TemplateException { 
067                TagImport ti=(TagImport) tag;
068                Attribute p = tag.getAttribute("prefix");
069                Attribute t = tag.getAttribute("taglib");
070                Attribute path = tag.getAttribute("path");
071                
072                if(p!=null || t!=null){
073                        if(p==null)
074                                throw new TemplateException(data.cfml,"Wrong Context, missing attribute [prefix] for tag "+tag.getFullname());
075                        if(t==null)
076                                throw new TemplateException(data.cfml,"Wrong Context, missing attribute [taglib] for tag "+tag.getFullname());
077                        
078                        if(path!=null)
079                                throw new TemplateException(data.cfml,"Wrong context, you have an invalid attributes constellation for the tag "+tag.getFullname()+", " +
080                                                "you cannot mix attribute [path] with attributes [taglib] and [prefix]");
081                        
082                        return executePT(config, tag, libTag, flibs, data.cfml);
083                }
084                if(path==null) throw new TemplateException(data.cfml,"Wrong context, you have an invalid attributes constellation for the tag "+tag.getFullname()+", " +
085                                "you need to define the attributes [prefix] and [taglib], the attribute [path] or simply define a attribute value");
086                
087                String strPath=ASMUtil.getAttributeString(tag,"path",null);
088        if(strPath==null) throw new TemplateException(data.cfml,"attribute [path] must be a constant value");
089        ti.setPath(strPath);
090        
091                return null;
092                
093        }
094        
095    private TagLib executePT(Config config,Tag tag, TagLibTag libTag, FunctionLib[] flibs,CFMLString cfml) throws TemplateException { 
096        
097        // Attribute prefix 
098        String nameSpace=ASMUtil.getAttributeString(tag,"prefix",null);
099        if(nameSpace==null) throw new TemplateException(cfml,"attribute [prefix] must be a constant value");
100        nameSpace=nameSpace.trim();
101        String nameSpaceSeparator=StringUtil.isEmpty(nameSpace)?"":":";
102        
103        
104        // Attribute taglib  
105        String textTagLib=ASMUtil.getAttributeString(tag,"taglib",null);
106        if(textTagLib==null) throw new TemplateException(cfml,"attribute [taglib] must be a constant value");
107        
108        textTagLib=textTagLib.replace('\\','/');
109        textTagLib=ConfigWebUtil.replacePlaceholder(textTagLib, config);
110        // File TagLib
111        String ext=ResourceUtil.getExtension(textTagLib,null);
112        boolean hasTldExtension="tld".equalsIgnoreCase(ext) || "tldx".equalsIgnoreCase(ext);
113        
114        Resource absFile=config.getResource(textTagLib);
115            // TLD
116                if(absFile.isFile()) return _executeTLD(config,absFile,nameSpace,nameSpaceSeparator,cfml);
117                // CTD
118                //else if(absFile.isDirectory()) return _executeCTD(absFile,textPrefix);
119                
120        
121                // Second Change            
122                if(textTagLib.startsWith("/")){
123            //config.getPhysical(textTagLib);
124                    PageSource ps = ((ConfigImpl)config).getPageSourceExisting(null,null,textTagLib,false,false,true,false);
125                    
126                    //config.getConfigDir()
127                    if(ps!=null) {
128                        if(ps.physcalExists()) {
129                                Resource file = ps.getPhyscalFile();
130                                        // TLD
131                                if(file.isFile()) return _executeTLD(config,file,nameSpace,nameSpaceSeparator,cfml);
132                        }
133                                // CTD
134                                if(!hasTldExtension)return _executeCTD(textTagLib,nameSpace,nameSpaceSeparator);
135                    }
136                }
137                else {
138                        Resource sourceFile=cfml.getPageSource().getPhyscalFile();
139                    if(sourceFile!=null) {
140                        Resource file = sourceFile.getParentResource().getRealResource(textTagLib);
141                                // TLD
142                        if(file.isFile()) return _executeTLD(config,file,nameSpace,nameSpaceSeparator,cfml);
143                                // CTD
144                        if(!hasTldExtension)return _executeCTD(textTagLib,nameSpace,nameSpaceSeparator);
145                    }
146                }
147            throw new TemplateException(cfml,"invalid definition of the attribute taglib ["+textTagLib+"]"); 
148    }
149
150    /**
151     * @param fileTagLib
152     * @return
153     * @throws EvaluatorException
154     */
155    private TagLib _executeTLD(Config config, Resource fileTagLib,String nameSpace,String nameSpaceSeparator, CFMLString cfml) throws TemplateException {
156        // change extesnion
157        String ext=ResourceUtil.getExtension(fileTagLib,null);
158        if("jar".equalsIgnoreCase(ext)) {
159            // check anchestor file
160                Resource newFileTagLib = ResourceUtil.changeExtension(fileTagLib,"tld");
161            if(newFileTagLib.exists())fileTagLib=newFileTagLib;
162            // check inside jar
163            else {
164                Resource tmp = getTLDFromJarAsFile(config,fileTagLib);
165                if(tmp!=null)fileTagLib=tmp;
166            }
167        }
168        
169        try {
170                
171            TagLib taglib = TagLibFactory.loadFromFile(fileTagLib);
172            taglib.setNameSpace(nameSpace);
173            taglib.setNameSpaceSeperator(nameSpaceSeparator);
174            return taglib;
175        } 
176        catch (TagLibException e) {
177                
178            throw new TemplateException(cfml,e.getMessage());
179        }
180    }
181    
182    private Resource getTLDFromJarAsFile(Config config, Resource jarFile) {
183        Resource jspTagLibDir = config.getTempDirectory().getRealResource("jsp-taglib");
184        if(!jspTagLibDir.exists())jspTagLibDir.mkdirs();
185        
186        String filename=null;
187        try {
188                        filename=Md5.getDigestAsString(ResourceUtil.getCanonicalPathEL(jarFile)+jarFile.lastModified());
189                } catch (IOException e) {}
190
191                Resource tldFile = jspTagLibDir.getRealResource(filename+".tld");
192                if(!tldFile.exists()) tldFile = jspTagLibDir.getRealResource(filename+".tldx");
193        if(tldFile.exists()) return tldFile;
194        
195        
196        byte[] barr = getTLDFromJarAsBarr(config,jarFile);
197        if(barr==null)return null;
198        
199        try {
200                        IOUtil.copy(new ByteArrayInputStream(barr), tldFile,true);
201                } 
202        catch (IOException e) {}
203        return tldFile;
204    }
205        
206    
207    private byte[] getTLDFromJarAsBarr(Config c,Resource jarFile) {  
208        ZipInputStream zis = null;
209        try {
210            zis = new ZipInputStream(IOUtil.toBufferedInputStream(jarFile.getInputStream()));
211            
212            byte[] buffer = new byte[0xffff];
213            int bytes_read;
214          
215            ZipEntry ze;
216            byte[] barr;
217            while((ze = zis.getNextEntry()) != null) {
218                if (!ze.isDirectory() && StringUtil.endsWithIgnoreCase(ze.getName(),".tld")) {
219                    SystemOut.printDate(c.getOutWriter(),"found tld in file ["+jarFile+"] at position "+ze.getName());
220                    ByteArrayOutputStream baos=new ByteArrayOutputStream();
221                    while((bytes_read = zis.read(buffer)) != -1)
222                        baos.write(buffer, 0, bytes_read);
223                    //String name = ze.getName().replace('\\', '/');
224                    barr=baos.toByteArray();
225                    zis.closeEntry();
226                    baos.close();
227                    return barr;
228                }
229            }
230        }
231        catch (IOException ioe) {}
232        finally {
233            Util.closeEL(zis);
234        }   
235        return null;
236    }
237    
238    
239    /**
240     * @param textTagLib
241     * @param nameSpace
242     * @return
243     */
244    private TagLib _executeCTD(String textTagLib, String nameSpace, String nameSpaceSeparator) {
245        return new CustomTagLib(textTagLib,nameSpace,nameSpaceSeparator);
246    }
247  
248}