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.xmlrpc; 020 021import org.apache.wiki.LinkCollector; 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.Page; 026import org.apache.wiki.api.spi.Wiki; 027import org.apache.wiki.auth.permissions.PagePermission; 028import org.apache.wiki.auth.permissions.PermissionFactory; 029import org.apache.wiki.pages.PageManager; 030import org.apache.wiki.render.RenderingManager; 031import org.apache.xmlrpc.XmlRpcException; 032 033import java.util.Calendar; 034import java.util.Date; 035import java.util.Hashtable; 036import java.util.Set; 037import java.util.Vector; 038import java.util.stream.Collectors; 039 040/** 041 * Provides handlers for all RPC routines. These routines are used by 042 * the UTF-8 interface. 043 * 044 * @since 1.6.13 045 */ 046 047public class RPCHandlerUTF8 extends AbstractRPCHandler { 048 049 public String getApplicationName() { 050 checkPermission( PagePermission.VIEW ); 051 return m_engine.getApplicationName(); 052 } 053 054 public Vector< String > getAllPages() { 055 checkPermission( PagePermission.VIEW ); 056 057 final Set< Page > pages = m_engine.getManager( PageManager.class ).getRecentChanges(); 058 059 return pages.stream().filter(p -> !(p instanceof Attachment)).map(Page::getName).collect(Collectors.toCollection(Vector::new)); 060 } 061 062 /** 063 * Encodes a single wiki page info into a Hashtable. 064 */ 065 @Override 066 protected Hashtable<String, Object> encodeWikiPage( final Page page ) { 067 final Hashtable<String, Object> ht = new Hashtable<>(); 068 ht.put( "name", page.getName() ); 069 070 final Date d = page.getLastModified(); 071 072 // 073 // Here we reset the DST and TIMEZONE offsets of the calendar. Unfortunately, I haven't thought of a better 074 // way to ensure that we're getting the proper date from the XML-RPC thingy, except to manually adjust the date. 075 // 076 final Calendar cal = Calendar.getInstance(); 077 cal.setTime( d ); 078 cal.add( Calendar.MILLISECOND, 079 - (cal.get( Calendar.ZONE_OFFSET ) + 080 (cal.getTimeZone().inDaylightTime( d ) ? cal.get( Calendar.DST_OFFSET ) : 0 )) ); 081 082 ht.put( "lastModified", cal.getTime() ); 083 ht.put( "version", page.getVersion() ); 084 085 if( page.getAuthor() != null ) { 086 ht.put( "author", page.getAuthor() ); 087 } 088 089 return ht; 090 } 091 092 @Override 093 public Vector< Hashtable< String, Object > > getRecentChanges( Date since ) { 094 checkPermission( PagePermission.VIEW ); 095 096 final Set< Page > pages = m_engine.getManager( PageManager.class ).getRecentChanges(); 097 final Vector< Hashtable< String, Object > > result = new Vector<>(); 098 099 final Calendar cal = Calendar.getInstance(); 100 cal.setTime( since ); 101 102 // 103 // Convert UTC to our time. 104 // 105 cal.add( Calendar.MILLISECOND, 106 (cal.get( Calendar.ZONE_OFFSET ) + 107 (cal.getTimeZone().inDaylightTime(since) ? cal.get( Calendar.DST_OFFSET ) : 0 ) ) ); 108 since = cal.getTime(); 109 110 for( final Page page : pages ) { 111 if( page.getLastModified().after( since ) && !( page instanceof Attachment ) ) { 112 result.add( encodeWikiPage( page ) ); 113 } 114 } 115 116 return result; 117 } 118 119 /** 120 * Simple helper method, turns the incoming page name into 121 * normal Java string, then checks page condition. 122 * 123 * @param pagename Page Name as an RPC string (URL-encoded UTF-8) 124 * @return Real page name, as Java string. 125 * @throws XmlRpcException, if there is something wrong with the page. 126 */ 127 private String parsePageCheckCondition( final String pagename ) throws XmlRpcException { 128 if( !m_engine.getManager( PageManager.class ).wikiPageExists(pagename) ) { 129 throw new XmlRpcException( ERR_NOPAGE, "No such page '"+pagename+"' found, o master." ); 130 } 131 132 final Page p = m_engine.getManager( PageManager.class ).getPage( pagename ); 133 134 checkPermission( PermissionFactory.getPagePermission( p, PagePermission.VIEW_ACTION ) ); 135 return pagename; 136 } 137 138 public Hashtable<String, Object> getPageInfo( final String pagename ) throws XmlRpcException { 139 return encodeWikiPage( m_engine.getManager( PageManager.class ).getPage( parsePageCheckCondition( pagename ) ) ); 140 } 141 142 public Hashtable<String, Object> getPageInfoVersion( String pagename, final int version ) throws XmlRpcException { 143 pagename = parsePageCheckCondition( pagename ); 144 145 return encodeWikiPage( m_engine.getManager( PageManager.class ).getPage( pagename, version ) ); 146 } 147 148 public String getPage( final String pagename ) throws XmlRpcException { 149 return m_engine.getManager( PageManager.class ).getPureText( parsePageCheckCondition( pagename ), -1 ); 150 } 151 152 public String getPageVersion( final String pagename, final int version ) throws XmlRpcException { 153 return m_engine.getManager( PageManager.class ).getPureText( parsePageCheckCondition( pagename ), version ); 154 } 155 156 public String getPageHTML( final String pagename ) throws XmlRpcException { 157 return m_engine.getManager( RenderingManager.class ).getHTML( parsePageCheckCondition( pagename ) ); 158 } 159 160 public String getPageHTMLVersion( final String pagename, final int version ) throws XmlRpcException { 161 return m_engine.getManager( RenderingManager.class ).getHTML( parsePageCheckCondition( pagename ), version ); 162 } 163 164 public Vector< Hashtable< String, String > > listLinks( String pagename ) throws XmlRpcException { 165 pagename = parsePageCheckCondition( pagename ); 166 167 final Page page = m_engine.getManager( PageManager.class ).getPage( pagename ); 168 final String pagedata = m_engine.getManager( PageManager.class ).getPureText( page ); 169 170 final LinkCollector localCollector = new LinkCollector(); 171 final LinkCollector extCollector = new LinkCollector(); 172 final LinkCollector attCollector = new LinkCollector(); 173 174 final Context context = Wiki.context().create( m_engine, page ); 175 m_engine.getManager( RenderingManager.class ).textToHTML( context, pagedata, localCollector, extCollector, attCollector ); 176 177 final Vector< Hashtable< String, String > > result = new Vector<>(); 178 179 // FIXME: Contains far too much common with RPCHandler. Refactor! 180 181 // 182 // Add local links. 183 // 184 for( final String link : localCollector.getLinks() ) { 185 final Hashtable<String, String> ht = new Hashtable<>(); 186 ht.put( "page", link ); 187 ht.put( "type", LINK_LOCAL ); 188 189 if( m_engine.getManager( PageManager.class ).wikiPageExists( link ) ) { 190 ht.put( "href", context.getViewURL( link ) ); 191 } else { 192 ht.put( "href", context.getURL( ContextEnum.PAGE_EDIT.getRequestContext(), link ) ); 193 } 194 195 result.add( ht ); 196 } 197 198 // 199 // Add links to inline attachments 200 // 201 for( final String link : attCollector.getLinks() ) { 202 final Hashtable<String, String> ht = new Hashtable<>(); 203 ht.put( "page", link ); 204 ht.put( "type", LINK_LOCAL ); 205 ht.put( "href", context.getURL( ContextEnum.PAGE_ATTACH.getRequestContext(), link ) ); 206 result.add( ht ); 207 } 208 209 // 210 // External links don't need to be changed into XML-RPC strings, simply because URLs are by definition ASCII. 211 // 212 for( final String link : extCollector.getLinks() ) { 213 final Hashtable<String, String> ht = new Hashtable<>(); 214 ht.put( "page", link ); 215 ht.put( "type", LINK_EXTERNAL ); 216 ht.put( "href", link ); 217 result.add( ht ); 218 } 219 220 return result; 221 } 222 223}