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.parser; 020 021import org.apache.log4j.Logger; 022import org.apache.oro.text.regex.Pattern; 023import org.apache.oro.text.regex.Perl5Matcher; 024import org.apache.wiki.api.core.Context; 025import org.apache.wiki.api.exceptions.ProviderException; 026 027import java.util.Arrays; 028import java.util.Comparator; 029import java.util.List; 030 031 032/** 033 * Link parsing operations. 034 * 035 * @since 2.10.3 036 */ 037public class LinkParsingOperations { 038 039 private static final Logger log = Logger.getLogger( LinkParsingOperations.class ); 040 private final Context wikiContext; 041 042 /** 043 * This list contains all IANA registered URI protocol types as of September 2004 + a few well-known extra types. 044 * 045 * JSPWiki recognises all of them as external links. 046 * 047 * This array is sorted during class load, so you can just dump here whatever you want in whatever order you want. 048 */ 049 static final String[] EXTERNAL_LINKS = { 050 "http:", "ftp:", "https:", "mailto:", 051 "news:", "file:", "rtsp:", "mms:", "ldap:", 052 "gopher:", "nntp:", "telnet:", "wais:", 053 "prospero:", "z39.50s", "z39.50r", "vemmi:", 054 "imap:", "nfs:", "acap:", "tip:", "pop:", 055 "dav:", "opaquelocktoken:", "sip:", "sips:", 056 "tel:", "fax:", "modem:", "soap.beep:", "soap.beeps", 057 "xmlrpc.beep", "xmlrpc.beeps", "urn:", "go:", 058 "h323:", "ipp:", "tftp:", "mupdate:", "pres:", 059 "im:", "mtqp", "smb:" 060 }; 061 062 static { 063 Arrays.sort( EXTERNAL_LINKS ); 064 } 065 066 public LinkParsingOperations( final Context wikiContext ) { 067 this.wikiContext = wikiContext; 068 } 069 070 /** 071 * Returns true, if the link in question is an access rule. 072 * 073 * @param link The link text 074 * @return {@code true}, if this represents an access rule. 075 */ 076 public boolean isAccessRule( final String link ) { 077 return link.startsWith("{ALLOW") || link.startsWith("{DENY"); 078 } 079 080 /** 081 * Returns true if the link is really command to insert a plugin. 082 * <P> 083 * Currently we just check if the link starts with "{INSERT", or just plain "{" but not "{$". 084 * 085 * @param link Link text, i.e. the contents of text between []. 086 * @return True, if this link seems to be a command to insert a plugin here. 087 */ 088 public boolean isPluginLink( final String link ) { 089 return link.startsWith( "{INSERT" ) || 090 ( link.startsWith( "{" ) && !link.startsWith( "{$" ) ); 091 } 092 093 /** 094 * Returns true if the link is a metadata link. 095 * 096 * @param link The link text 097 * @return {@code true}, if this represents a metadata link. 098 */ 099 public boolean isMetadata( final String link ) { 100 return link.startsWith( "{SET" ); 101 } 102 103 /** 104 * Returns true if the link is really command to insert a variable. 105 * <P> 106 * Currently we just check if the link starts with "{$". 107 * 108 * @param link The link text 109 * @return {@code true}, if this represents a variable link. 110 */ 111 public boolean isVariableLink( final String link ) { 112 return link.startsWith( "{$" ); 113 } 114 115 /** 116 * Returns true, if this Link represents an InterWiki link (of the form wiki:page). 117 * 118 * @return {@code true}, if this Link represents an InterWiki link, {@code false} otherwise. 119 */ 120 public boolean isInterWikiLink( final String page ) { 121 return interWikiLinkAt( page ) != -1; 122 } 123 124 /** 125 * Returns true, if this Link represents an InterWiki link (of the form wiki:page). 126 * 127 * @return {@code true}, if this Link represents an InterWiki link, {@code false} otherwise. 128 */ 129 public int interWikiLinkAt( final String page ) { 130 return page.indexOf( ':' ); 131 } 132 133 /** 134 * Figures out if a link is an off-site link. This recognizes the most common protocols by checking how it starts. 135 * 136 * @param page The link to check. 137 * @return true, if this is a link outside of this wiki. 138 */ 139 public boolean isExternalLink( final String page ) { 140 final int idx = Arrays.binarySearch( EXTERNAL_LINKS, page, new StartingComparator() ); 141 142 // We need to check here once again; otherwise we might get a match for something like "h". 143 return idx >= 0 && page.startsWith( EXTERNAL_LINKS[ idx ] ); 144 } 145 146 /** 147 * Matches the given link to the list of image name patterns to determine whether it should be treated as an inline image or not. 148 */ 149 public boolean isImageLink( String link, final boolean isImageInlining, final List< Pattern > inlineImagePatterns ) { 150 if( isImageInlining ) { 151 link = link.toLowerCase(); 152 for( final Pattern p : inlineImagePatterns ) { 153 if( new Perl5Matcher().matches( link, p ) ) { 154 return true; 155 } 156 } 157 } 158 159 return false; 160 } 161 162 /** 163 * Returns {@code true}, if the link name exists; otherwise it returns {@code false}. 164 * 165 * @param page link name 166 * @return {@code true}, if the link name exists; otherwise it returns {@code false}. 167 */ 168 public boolean linkExists( final String page ) { 169 if( page == null || page.length() == 0 ) { 170 return false; 171 } 172 try { 173 return wikiContext.getEngine().getFinalPageName( page ) != null; 174 } catch( final ProviderException e ) { 175 log.warn( "TranslatorReader got a faulty page name [" + page + "]!", e ); 176 return false; 177 } 178 } 179 180 /** 181 * Returns link name, if it exists; otherwise it returns {@code null}. 182 * 183 * @param page link name 184 * @return link name, if it exists; otherwise it returns {@code null}. 185 */ 186 public String linkIfExists( final String page ) { 187 if( page == null || page.length() == 0 ) { 188 return null; 189 } 190 try { 191 return wikiContext.getEngine().getFinalPageName( page ); 192 } catch( final ProviderException e ) { 193 log.warn( "TranslatorReader got a faulty page name [" + page + "]!", e ); 194 return null; 195 } 196 } 197 198 /** 199 * Compares two Strings, and if one starts with the other, then returns 0. Otherwise just like the normal Comparator for strings. 200 */ 201 private static class StartingComparator implements Comparator< String > { 202 203 /** 204 * {@inheritDoc} 205 * 206 * @see Comparator#compare(Object, Object) 207 */ 208 @Override 209 public int compare( final String s1, final String s2 ) { 210 if( s1.length() > s2.length() ) { 211 if( s1.startsWith( s2 ) && s2.length() > 1 ) { 212 return 0; 213 } 214 } else if( s2.startsWith( s1 ) && s1.length() > 1 ) { 215 return 0; 216 } 217 218 return s1.compareTo( s2 ); 219 } 220 221 } 222 223}