001/*
002    Licensed to the Apache Software Foundation (ASF) under one
003    or more contributor license agreements.  See the NOTICE file
004    distributed with this work for additional information
005    regarding copyright ownership.  The ASF licenses this file
006    to you under the Apache License, Version 2.0 (the
007    "License"); you may not use this file except in compliance
008    with the License.  You may obtain a copy of the License at
009
010       http://www.apache.org/licenses/LICENSE-2.0
011
012    Unless required by applicable law or agreed to in writing,
013    software distributed under the License is distributed on an
014    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015    KIND, either express or implied.  See the License for the
016    specific language governing permissions and limitations
017    under the License.
018 */
019package org.apache.wiki.plugin;
020
021import java.util.Locale;
022import org.apache.wiki.api.core.Attachment;
023import org.apache.wiki.api.core.Context;
024import org.apache.wiki.api.core.ContextEnum;
025import org.apache.wiki.api.core.Engine;
026import org.apache.wiki.api.exceptions.PluginException;
027import org.apache.wiki.api.exceptions.ProviderException;
028import org.apache.wiki.api.plugin.Plugin;
029import org.apache.wiki.attachment.AttachmentManager;
030import org.apache.wiki.parser.MarkupParser;
031import org.apache.wiki.util.TextUtil;
032
033import java.util.Map;
034import java.util.ResourceBundle;
035
036
037/**
038 *  Provides an image plugin for better control than is possible with a simple image inclusion.
039 *  <br> Most parameters are equivalents of the html image attributes.
040 *
041 *  <p>Parameters : </p>
042 *  <ul>
043 *  <li><b>src</b> - the source (a URL) of the image (required parameter)</li>
044 *  <li><b>align</b> - the alignment of the image</li>
045 *  <li><b>height</b> - the height of the image</li>
046 *  <li><b>width</b> - the width of the image</li>
047 *  <li><b>alt</b> - alternate text</li>
048 *  <li><b>caption</b> - the caption for the image</li>
049 *  <li><b>link</b> - the hyperlink for the image</li>
050 *  <li><b>target</b> - the target (frame) to be used for opening the image</li>
051 *  <li><b>style</b> - the style attribute of the image</li>
052 *  <li><b>class</b> - the associated class for the image</li>
053 *  <li><b>border</b> - the border for the image</li>
054 *  <li><b>title</b> - the title for the image, can be presented as a tooltip to the user</li>
055 *  </ul>
056 *
057 *  @since 2.1.4.
058 */
059// FIXME: It is not yet possible to do wiki internal links.  In order to do this cleanly, a TranslatorReader revamp is needed.
060public class Image implements Plugin {
061
062    /** The parameter name for setting the src.  Value is <tt>{@value}</tt>. */
063    public static final String PARAM_SRC      = "src";
064    /** The parameter name for setting the align parameter.  Value is <tt>{@value}</tt>. */
065    public static final String PARAM_ALIGN    = "align";
066    /** The parameter name for setting the height.  Value is <tt>{@value}</tt>. */
067    public static final String PARAM_HEIGHT   = "height";
068    /** The parameter name for setting the width.  Value is <tt>{@value}</tt>. */
069    public static final String PARAM_WIDTH    = "width";
070    /** The parameter name for setting the alt.  Value is <tt>{@value}</tt>. */
071    public static final String PARAM_ALT      = "alt";
072    /** The parameter name for setting the caption.  Value is <tt>{@value}</tt>. */
073    public static final String PARAM_CAPTION  = "caption";
074    /** The parameter name for setting the link.  Value is <tt>{@value}</tt>. */
075    public static final String PARAM_LINK     = "link";
076    /** The parameter name for setting the target.  Value is <tt>{@value}</tt>. */
077    public static final String PARAM_TARGET   = "target";
078    /** The parameter name for setting the style.  Value is <tt>{@value}</tt>. */
079    public static final String PARAM_STYLE    = "style";
080    /** The parameter name for setting the class.  Value is <tt>{@value}</tt>. */
081    public static final String PARAM_CLASS    = "class";
082    /** The parameter name for setting the border.  Value is <tt>{@value}</tt>. */
083    public static final String PARAM_BORDER   = "border";
084    /** The parameter name for setting the title.  Value is <tt>{@value}</tt>. */
085    public static final String PARAM_TITLE    = "title";
086
087    /**
088     *  This method is used to clean away things like quotation marks which
089     *  a malicious user could use to stop processing and insert javascript.
090     */
091    private static String getCleanParameter( final Map< String, String > params, final String paramId ) {
092        return TextUtil.replaceEntities( params.get( paramId ) );
093    }
094    
095    @Override
096    public String getDisplayName(Locale locale) {
097        final ResourceBundle rb = ResourceBundle.getBundle(PluginManager.PLUGIN_I18N_RESOURCE, locale);
098        return rb.getString(this.getClass().getSimpleName());
099    }
100    
101    @Override
102    public String  getSnipExample() {
103        return  "Image src='{image.jpg}'";
104    }
105    
106    private boolean needsSanitization(String link) {
107        String testVal = link.toLowerCase().replaceAll("\\s+", "").trim();
108        if (testVal.startsWith("data")
109                || testVal.startsWith("javascript")
110                || testVal.startsWith("vbscript")) {
111            return true;
112        }
113        return false;
114    }
115
116    /**
117     *  {@inheritDoc}
118     */
119    @Override
120    public String execute( final Context context, final Map<String, String> params ) throws PluginException {
121        final Engine engine  = context.getEngine();
122        String src           = getCleanParameter( params, PARAM_SRC );
123        final String align   = getCleanParameter( params, PARAM_ALIGN );
124        final String ht      = getCleanParameter( params, PARAM_HEIGHT );
125        final String wt      = getCleanParameter( params, PARAM_WIDTH );
126        final String alt     = getCleanParameter( params, PARAM_ALT );
127        final String caption = getCleanParameter( params, PARAM_CAPTION );
128        String link          = getCleanParameter( params, PARAM_LINK );
129        String target        = getCleanParameter( params, PARAM_TARGET );
130        final String style   = getCleanParameter( params, PARAM_STYLE );
131        final String cssclass= getCleanParameter( params, PARAM_CLASS );
132        final String border  = getCleanParameter( params, PARAM_BORDER );
133        final String title   = getCleanParameter( params, PARAM_TITLE );
134
135        if( src == null ) {
136            throw new PluginException("Parameter 'src' is required for Image plugin");
137        }
138
139        //if( cssclass == null ) cssclass = "imageplugin";
140
141        if( target != null && !validTargetValue(target) ) {
142            target = null; // not a valid value so ignore
143        }
144
145        try {
146            final AttachmentManager mgr = engine.getManager( AttachmentManager.class );
147            final Attachment att = mgr.getAttachmentInfo( context, src );
148
149            if( att != null ) {
150                src = context.getURL( ContextEnum.PAGE_ATTACH.getRequestContext(), att.getName() );
151            }
152        } catch( final ProviderException e ) {
153            throw new PluginException( "Attachment info failed: " + e.getMessage() );
154        }
155
156        final StringBuilder result = new StringBuilder();
157
158        result.append( "<table border=\"0\" class=\"imageplugin\"" );
159
160        if( title != null ) {
161            result.append( " title=\"" ).append( title ).append( "\"" );
162        }
163
164        if( align != null ) {
165            if( align.equals( "center" ) ) {
166                result.append( " style=\"margin-left: auto; margin-right: auto; text-align:center; vertical-align:middle;\"" );
167            } else {
168                result.append( " style=\"float:" ).append( align ).append( ";\"" );
169            }
170        }
171
172        result.append( ">\n" );
173
174        if( caption != null ) {
175            result.append( "<caption>" ).append( caption ).append( "</caption>\n" );
176        }
177
178        // move css class and style to the container of the image, so it doesn't affect the caption
179        result.append( "<tr><td" );
180
181        if( cssclass != null ) {
182            result.append( " class=\"" ).append( cssclass ).append( "\"" );
183        }
184
185        if( style != null ) {
186            result.append( " style=\"" ).append( style );
187
188            // Make sure that we add a ";" to the end of the style string
189            if( result.charAt( result.length()-1 ) != ';' ) {
190                result.append( ";" );
191            }
192
193            result.append("\"");
194        }
195
196        result.append( ">" );
197
198        if( link != null ) {
199            if( !context.getBooleanWikiProperty( MarkupParser.PROP_ALLOWHTML, false ) ) {
200                if (needsSanitization(link)) {
201                    link = "http://invalid_url" + link;
202                }
203            }
204            result.append( "<a href=\"" ).append( link ).append( "\"" );
205            if( target != null ) {
206                result.append( " target=\"" ).append( target ).append( "\"" );
207            }
208            result.append(">");
209        }
210
211        if(!context.getBooleanWikiProperty(MarkupParser.PROP_ALLOWHTML, false)) {
212            if (needsSanitization(src)) {
213                src = "http://invalid_url" + src;
214            }
215        }
216        result.append( "<img src=\"" ).append( src ).append( "\"" );
217
218        if( ht != null ) {
219            result.append( " height=\"" ).append( ht ).append( "\"" );
220        }
221        if( wt != null ) {
222            result.append( " width=\"" ).append( wt ).append( "\"" );
223        }
224        if( alt != null ) {
225            result.append( " alt=\"" ).append( alt ).append( "\"" );
226        }
227        if( border != null ) {
228            result.append( " border=\"" ).append( border ).append( "\"" );
229        }
230        // if( map != null )    result.append(" map=\""+map+"\"");
231
232        result.append(" />");
233        if( link != null ) {
234            result.append("</a>");
235        }
236        result.append("</td></tr>\n");
237        result.append("</table>\n");
238
239        return result.toString();
240    }
241
242    private boolean validTargetValue( final String s ) {
243        if( s.equals("_blank")
244            || s.equals("_self")
245            || s.equals("_parent")
246            || s.equals("_top") ) {
247            return true;
248        } else if( !s.isEmpty() ) { // check [a-zA-z]
249            final char c = s.charAt(0);
250            return Character.isLowerCase(c) || Character.isUpperCase(c);
251        }
252        return false;
253    }
254
255}