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 java.io.ByteArrayInputStream; 022import java.util.Collections; 023import java.util.Date; 024import java.util.Hashtable; 025import java.util.Iterator; 026import java.util.List; 027 028import org.apache.log4j.Logger; 029import org.apache.wiki.WikiContext; 030import org.apache.wiki.WikiEngine; 031import org.apache.wiki.WikiPage; 032import org.apache.wiki.api.exceptions.ProviderException; 033import org.apache.wiki.attachment.Attachment; 034import org.apache.wiki.attachment.AttachmentManager; 035import org.apache.wiki.auth.AuthenticationManager; 036import org.apache.wiki.auth.AuthorizationManager; 037import org.apache.wiki.auth.WikiSecurityException; 038import org.apache.wiki.auth.permissions.PermissionFactory; 039import org.apache.wiki.pages.PageTimeComparator; 040import org.apache.wiki.plugin.WeblogEntryPlugin; 041import org.apache.wiki.plugin.WeblogPlugin; 042import org.apache.xmlrpc.XmlRpcException; 043 044/** 045 * Provides handlers for all RPC routines of the MetaWeblog API. 046 * <P> 047 * JSPWiki does not support categories, and therefore we always return 048 * an empty list for getCategories(). Note also that this API is not 049 * suitable for general Wiki editing, since JSPWiki formats the entries 050 * in a wiki-compatible manner. And you cannot choose your page names 051 * either. Since 2.1.94 the entire MetaWeblog API is supported. 052 * 053 * @since 2.1.7 054 */ 055 056public class MetaWeblogHandler 057 implements WikiRPCHandler 058{ 059 private static Logger log = Logger.getLogger( MetaWeblogHandler.class ); 060 061 private WikiContext m_context; 062 063 /** 064 * {@inheritDoc} 065 */ 066 public void initialize( WikiContext context ) 067 { 068 m_context = context; 069 } 070 071 /** 072 * Does a quick check against the current user 073 * and does he have permissions to do the stuff 074 * that he really wants to. 075 * <p> 076 * If there is no authentication enabled, returns normally. 077 * 078 * @throw XmlRpcException with the correct error message, if auth fails. 079 */ 080 private void checkPermissions( WikiPage page, 081 String username, 082 String password, 083 String permission ) 084 throws XmlRpcException 085 { 086 try 087 { 088 AuthenticationManager amm = m_context.getEngine().getAuthenticationManager(); 089 AuthorizationManager mgr = m_context.getEngine().getAuthorizationManager(); 090 091 if( amm.login( m_context.getWikiSession(), m_context.getHttpRequest(), username, password ) ) 092 { 093 if( !mgr.checkPermission( m_context.getWikiSession(), PermissionFactory.getPagePermission( page, permission ) )) 094 { 095 throw new XmlRpcException( 1, "No permission" ); 096 } 097 } 098 else 099 { 100 throw new XmlRpcException( 1, "Unknown login" ); 101 } 102 } 103 catch( WikiSecurityException e ) 104 { 105 throw new XmlRpcException( 1, e.getMessage(), e ); 106 } 107 return; 108 } 109 110 /** 111 * JSPWiki does not support categories, therefore JSPWiki 112 * always returns an empty list for categories. 113 * 114 * @param blogid The id of the blog. 115 * @param username The username to use 116 * @param password The password 117 * @throws XmlRpcException If something goes wrong 118 * @return An empty hashtable. 119 */ 120 public Hashtable getCategories( String blogid, 121 String username, 122 String password ) 123 throws XmlRpcException 124 { 125 WikiPage page = m_context.getEngine().getPage( blogid ); 126 127 checkPermissions( page, username, password, "view" ); 128 129 Hashtable ht = new Hashtable(); 130 131 return ht; 132 } 133 134 private String getURL( String page ) 135 { 136 return m_context.getEngine().getURL( WikiContext.VIEW, 137 page, 138 null, 139 true ); // Force absolute urls 140 } 141 142 /** 143 * Takes a wiki page, and creates a metaWeblog struct 144 * out of it. 145 * @param page The actual entry page 146 * @return A metaWeblog entry struct. 147 */ 148 private Hashtable<String,Object> makeEntry( WikiPage page ) 149 { 150 Hashtable<String, Object> ht = new Hashtable<String, Object>(); 151 152 WikiPage firstVersion = m_context.getEngine().getPage( page.getName(), 1 ); 153 154 ht.put("dateCreated", firstVersion.getLastModified()); 155 ht.put("link", getURL(page.getName())); 156 ht.put("permaLink", getURL(page.getName())); 157 ht.put("postid", page.getName()); 158 ht.put("userid", page.getAuthor()); 159 160 String pageText = m_context.getEngine().getText(page.getName()); 161 String title = ""; 162 int firstLine = pageText.indexOf('\n'); 163 164 if( firstLine > 0 ) 165 { 166 title = pageText.substring( 0, firstLine ); 167 } 168 169 if( title.trim().length() == 0 ) title = page.getName(); 170 171 // Remove wiki formatting 172 while( title.startsWith("!") ) title = title.substring(1); 173 174 ht.put("title", title); 175 ht.put("description", pageText); 176 177 return ht; 178 } 179 180 /** 181 * Returns a list of the recent posts to this weblog. 182 * 183 * @param blogid The id of the blog. 184 * @param username The username to use 185 * @param password The password 186 * @param numberOfPosts How many posts to find 187 * @throws XmlRpcException If something goes wrong 188 * @return As per MetaweblogAPI specification 189 */ 190 191 // FIXME: The implementation is suboptimal, as it 192 // goes through all of the blog entries. 193 194 public Hashtable getRecentPosts( String blogid, 195 String username, 196 String password, 197 int numberOfPosts) 198 throws XmlRpcException 199 { 200 Hashtable<String, Hashtable<String, Object>> result = new Hashtable<>(); 201 202 log.info( "metaWeblog.getRecentPosts() called"); 203 204 WikiPage page = m_context.getEngine().getPage( blogid ); 205 206 checkPermissions( page, username, password, "view" ); 207 208 try 209 { 210 WeblogPlugin plugin = new WeblogPlugin(); 211 212 List<WikiPage> changed = plugin.findBlogEntries(m_context.getEngine(), 213 blogid, 214 new Date(0L), 215 new Date()); 216 217 Collections.sort( changed, new PageTimeComparator() ); 218 219 int items = 0; 220 for( Iterator< WikiPage > i = changed.iterator(); i.hasNext() && items < numberOfPosts; items++ ) 221 { 222 WikiPage p = i.next(); 223 224 result.put( "entry", makeEntry( p ) ); 225 } 226 227 } 228 catch( ProviderException e ) 229 { 230 log.error( "Failed to list recent posts", e ); 231 232 throw new XmlRpcException( 0, e.getMessage() ); 233 } 234 235 return result; 236 } 237 238 /** 239 * Adds a new post to the blog. 240 * 241 * @param blogid The id of the blog. 242 * @param username The username to use 243 * @param password The password 244 * @param content As per Metaweblogapi contract 245 * @param publish This parameter is ignored for JSPWiki. 246 * @return Returns an empty string 247 * @throws XmlRpcException If something goes wrong 248 */ 249 public String newPost( String blogid, 250 String username, 251 String password, 252 Hashtable content, 253 boolean publish ) 254 throws XmlRpcException 255 { 256 log.info("metaWeblog.newPost() called"); 257 WikiEngine engine = m_context.getEngine(); 258 259 WikiPage page = engine.getPage( blogid ); 260 checkPermissions( page, username, password, "createPages" ); 261 262 try 263 { 264 WeblogEntryPlugin plugin = new WeblogEntryPlugin(); 265 266 String pageName = plugin.getNewEntryPage( engine, blogid ); 267 268 WikiPage entryPage = new WikiPage( engine, pageName ); 269 entryPage.setAuthor( username ); 270 271 WikiContext context = new WikiContext( engine, entryPage ); 272 273 StringBuilder text = new StringBuilder(); 274 text.append( "!"+content.get("title") ); 275 text.append( "\n\n" ); 276 text.append( content.get("description") ); 277 278 log.debug("Writing entry: "+text); 279 280 engine.saveText( context, text.toString() ); 281 } 282 catch( Exception e ) 283 { 284 log.error("Failed to create weblog entry",e); 285 throw new XmlRpcException( 0, "Failed to create weblog entry: "+e.getMessage() ); 286 } 287 288 return ""; // FIXME: 289 } 290 291 /** 292 * Creates an attachment and adds it to the blog. The attachment 293 * is created into the main blog page, not the actual post page, 294 * because we do not know it at this point. 295 * 296 * @param blogid The id of the blog. 297 * @param username The username to use 298 * @param password The password 299 * @param content As per the MetaweblogAPI contract 300 * @return As per the MetaweblogAPI contract 301 * @throws XmlRpcException If something goes wrong 302 * 303 */ 304 public Hashtable newMediaObject( String blogid, 305 String username, 306 String password, 307 Hashtable content ) 308 throws XmlRpcException 309 { 310 WikiEngine engine = m_context.getEngine(); 311 String url = ""; 312 313 log.info("metaWeblog.newMediaObject() called"); 314 315 WikiPage page = engine.getPage( blogid ); 316 checkPermissions( page, username, password, "upload" ); 317 318 String name = (String) content.get( "name" ); 319 byte[] data = (byte[]) content.get( "bits" ); 320 321 AttachmentManager attmgr = engine.getAttachmentManager(); 322 323 try 324 { 325 Attachment att = new Attachment( engine, blogid, name ); 326 att.setAuthor( username ); 327 attmgr.storeAttachment( att, new ByteArrayInputStream( data ) ); 328 329 url = engine.getURL( WikiContext.ATTACH, att.getName(), null, true ); 330 } 331 catch( Exception e ) 332 { 333 log.error( "Failed to upload attachment", e ); 334 throw new XmlRpcException( 0, "Failed to upload media object: "+e.getMessage() ); 335 } 336 337 Hashtable<String, Object> result = new Hashtable<String, Object>(); 338 result.put("url", url); 339 340 return result; 341 } 342 343 344 /** 345 * Allows the user to edit a post. It does not allow general 346 * editability of wiki pages, because of the limitations of the 347 * metaWeblog API. 348 */ 349 boolean editPost( String postid, 350 String username, 351 String password, 352 Hashtable content, 353 boolean publish ) 354 throws XmlRpcException 355 { 356 WikiEngine engine = m_context.getEngine(); 357 log.info("metaWeblog.editPost("+postid+") called"); 358 359 // FIXME: Is postid correct? Should we determine it from the page name? 360 WikiPage page = engine.getPage( postid ); 361 checkPermissions( page, username, password, "edit" ); 362 363 try 364 { 365 WikiPage entryPage = (WikiPage)page.clone(); 366 entryPage.setAuthor( username ); 367 368 WikiContext context = new WikiContext( engine, entryPage ); 369 370 StringBuilder text = new StringBuilder(); 371 text.append( "!"+content.get("title") ); 372 text.append( "\n\n" ); 373 text.append( content.get("description") ); 374 375 log.debug("Updating entry: "+text); 376 377 engine.saveText( context, text.toString() ); 378 } 379 catch( Exception e ) 380 { 381 log.error("Failed to create weblog entry",e); 382 throw new XmlRpcException( 0, "Failed to update weblog entry: "+e.getMessage() ); 383 } 384 385 return true; 386 } 387 388 /** 389 * Gets the text of any page. The title of the page is parsed 390 * (if any is provided). 391 */ 392 Hashtable getPost( String postid, 393 String username, 394 String password ) 395 throws XmlRpcException 396 { 397 String wikiname = "FIXME"; 398 399 WikiPage page = m_context.getEngine().getPage( wikiname ); 400 401 checkPermissions( page, username, password, "view" ); 402 403 return makeEntry( page ); 404 } 405}