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.rpc.atom; 020 021import com.rometools.rome.feed.synd.SyndContent; 022import com.rometools.rome.feed.synd.SyndContentImpl; 023import com.rometools.rome.feed.synd.SyndEntry; 024import com.rometools.rome.feed.synd.SyndEntryImpl; 025import com.rometools.rome.feed.synd.SyndFeed; 026import com.rometools.rome.feed.synd.SyndFeedImpl; 027import com.rometools.rome.feed.synd.SyndLink; 028import com.rometools.rome.feed.synd.SyndLinkImpl; 029import com.rometools.rome.io.SyndFeedInput; 030import com.rometools.rome.io.SyndFeedOutput; 031import org.apache.logging.log4j.LogManager; 032import org.apache.logging.log4j.Logger; 033import org.apache.wiki.api.core.Context; 034import org.apache.wiki.api.core.Engine; 035import org.apache.wiki.api.core.Page; 036import org.apache.wiki.api.exceptions.ProviderException; 037import org.apache.wiki.api.exceptions.WikiException; 038import org.apache.wiki.api.spi.Wiki; 039import org.apache.wiki.pages.PageManager; 040import org.apache.wiki.plugin.WeblogEntryPlugin; 041import org.apache.wiki.plugin.WeblogPlugin; 042import org.apache.wiki.util.TextUtil; 043 044import jakarta.servlet.ServletConfig; 045import jakarta.servlet.ServletException; 046import jakarta.servlet.http.HttpServlet; 047import jakarta.servlet.http.HttpServletRequest; 048import jakarta.servlet.http.HttpServletResponse; 049import java.io.IOException; 050import java.io.InputStreamReader; 051import java.util.ArrayList; 052import java.util.Collection; 053import java.util.Date; 054import java.util.List; 055import org.apache.wiki.util.HttpUtil; 056 057 058/** 059 * Handles incoming requests for the Atom API. This class uses the "sandler" Atom API implementation. 060 * 061 * @since 2.1.97 062 */ 063// FIXME: Rewrite using some other library 064public class AtomAPIServlet extends HttpServlet { 065 066 private static final Logger LOG = LogManager.getLogger( AtomAPIServlet.class ); 067 068 private static final long serialVersionUID = 0L; 069 070 private Engine m_engine; 071 072 /** 073 * {@inheritDoc} 074 */ 075 @Override 076 public void init( final ServletConfig config ) throws ServletException { 077 m_engine = Wiki.engine().find( config ); 078 } 079 080 /** 081 * Takes the name of the page from the request URI. 082 * The initial slash is also removed. If there is no page, 083 * returns null. 084 */ 085 private String getPageName( final HttpServletRequest request ) { 086 String name = request.getPathInfo(); 087 if( name == null || name.length() <= 1 ) { 088 return null; 089 } else if( name.charAt( 0 ) == '/' ) { 090 name = name.substring( 1 ); 091 } 092 093 return TextUtil.urlDecodeUTF8( name ); 094 } 095 096 /** 097 * Implements the PostURI of the Atom spec. 098 * <p> 099 * Implementation notes: 100 * <ul> 101 * <li>Only fetches the first content. All other contents are ignored. 102 * <li>Assumes that incoming code is plain text or WikiMarkup, not html. 103 * </ul> 104 * 105 * {@inheritDoc} 106 */ 107 @Override 108 public void doPost( final HttpServletRequest request, final HttpServletResponse response ) throws ServletException { 109 LOG.debug( "Received POST to AtomAPIServlet" ); 110 111 try { 112 final String blogid = getPageName( request ); 113 final Page page = m_engine.getManager( PageManager.class ).getPage( blogid ); 114 if( page == null ) { 115 throw new ServletException( "Page " + blogid + " does not exist, cannot add blog post." ); 116 } 117 118 // FIXME: Do authentication here 119 SyndFeed entry = new SyndFeedInput().build(new InputStreamReader(request.getInputStream() )); 120 121 122 // Fetch the obligatory parts of the content. 123 final String title = entry.getTitle(); 124 final SyndEntry content = entry.getEntries().get( 0 ); 125 final String author = entry.getAuthor(); 126 127 // FIXME: Sandler 0.5 does not support generator 128 // Generate new blog entry. 129 final WeblogEntryPlugin plugin = new WeblogEntryPlugin(); 130 final String pageName = plugin.getNewEntryPage( m_engine, blogid ); 131 final String username = author; 132 final Page entryPage = Wiki.contents().page( m_engine, pageName ); 133 entryPage.setAuthor( username ); 134 135 final Context context = Wiki.context().create( m_engine, request, entryPage ); 136 final StringBuilder text = new StringBuilder(); 137 text.append( "!" ) 138 .append( title ) 139 .append( "\n\n" ) 140 .append( content ); 141 LOG.debug( "Writing entry: " + text ); 142 m_engine.getManager( PageManager.class ).saveText( context, text.toString() ); 143 } catch( final IOException e ) { 144 LOG.error("I/O exception",e); 145 throw new ServletException("Could not get body of request"); 146 } catch( final WikiException e ) { 147 LOG.error("Provider exception while posting",e); 148 throw new ServletException("JSPWiki cannot save the entry"); 149 } catch( final Exception e ) { 150 LOG.error("Received faulty Atom entry",e); 151 throw new ServletException("Faulty Atom entry"); 152 } 153 } 154 155 /** 156 * Handles HTTP GET. However, we do not respond to GET requests, 157 * other than to show an explanatory text. 158 * 159 * {@inheritDoc} 160 */ 161 @Override 162 public void doGet( final HttpServletRequest request, final HttpServletResponse response ) throws ServletException { 163 LOG.debug( "Received HTTP GET to AtomAPIServlet" ); 164 final String blogid = getPageName( request ); 165 LOG.debug( "Requested page " + blogid ); 166 try { 167 if( blogid == null ) { 168 final SyndFeed feed = listBlogs(request); 169 response.setContentType( "application/x.atom+xml; charset=UTF-8" ); 170 response.setHeader("Content-Disposition", " attachment; filename=\"atom.xml\""); 171 feed.setFeedType("atom_0.3"); 172 SyndFeedOutput output = new SyndFeedOutput(); 173 output.output(feed,response.getWriter()); 174 } else { 175 final SyndEntry entry = getBlogEntry( blogid ); 176 response.setContentType( "application/x.atom+xml; charset=UTF-8" ); 177 response.setHeader("Content-Disposition", " attachment; filename=\"atom.xml\""); 178 SyndFeed atom = new SyndFeedImpl(); 179 atom.setFeedType("atom_0.3"); 180 atom.getEntries().add(entry); 181 SyndFeedOutput output = new SyndFeedOutput(); 182 output.output(atom,response.getWriter()); 183 } 184 response.getWriter().flush(); 185 } catch( final Exception e ) { 186 LOG.error( "Unable to generate response", e ); 187 throw new ServletException( "Internal problem - whack Janne on the head to get a better error report"); 188 } 189 } 190 191 private SyndEntry getBlogEntry( final String entryid ) { 192 final Page page = m_engine.getManager( PageManager.class ).getPage( entryid ); 193 final Page firstVersion = m_engine.getManager( PageManager.class ).getPage( entryid, 1 ); 194 final SyndEntry entry = new SyndEntryImpl(); 195 final String pageText = m_engine.getManager( PageManager.class ).getText(page.getName()); 196 final int firstLine = pageText.indexOf('\n'); 197 198 String title = ""; 199 if( firstLine > 0 ) { 200 title = pageText.substring( 0, firstLine ); 201 } 202 203 if( title.trim().isEmpty() ) { 204 title = page.getName(); 205 } 206 207 // Remove wiki formatting 208 while( title.startsWith("!") ) { 209 title = title.substring(1); 210 } 211 212 entry.setTitle( title ); 213 entry.setPublishedDate(firstVersion.getLastModified() ); 214 entry.setUpdatedDate(page.getLastModified() ); 215 entry.setAuthor( page.getAuthor()); 216 List<SyndContent> list = new ArrayList<>(); 217 list.add(new SyndContentImpl()); 218 list.get(0).setValue(pageText); 219 220 return entry; 221 } 222 223 /** 224 * Creates and outputs a full list of all available blogs 225 */ 226 private SyndFeed listBlogs( final HttpServletRequest request ) throws ProviderException{ 227 final Collection< Page > pages = m_engine.getManager( PageManager.class ).getAllPages(); 228 final SyndFeed feed = new SyndFeedImpl(); 229 feed.setTitle("List of blogs at this site"); 230 feed.setPublishedDate( new Date() ); 231 232 for( final Page p : pages ) { 233 // List only weblogs 234 // FIXME: Unfortunately, a weblog is not known until it has een executed once, because plugins are off during the initial startup phase. 235 LOG.debug( p.getName() + " = " + p.getAttribute( WeblogPlugin.ATTR_ISWEBLOG ) ); 236 237 if( !( "true".equals( p.getAttribute( WeblogPlugin.ATTR_ISWEBLOG ) ) ) && 238 !( "true".equals( p.getAttribute( "@" + WeblogPlugin.ATTR_ISWEBLOG ) ) )) { 239 continue; 240 } 241 242 final String encodedName = TextUtil.urlEncodeUTF8( p.getName() ); 243 final Context context = Wiki.context().create( m_engine, p ); 244 final String title = TextUtil.replaceEntities( org.apache.wiki.rss.Feed.getSiteName( context ) ); 245 //FIXME this needs to be an absolute URL not a relative one 246 final SyndLink postlink = createLink( "service.post", HttpUtil.getAbsoluteUrl(request, m_engine.getBaseURL()) + "/atom/" + encodedName, title ); 247 final SyndLink editlink = createLink( "service.edit", HttpUtil.getAbsoluteUrl(request, m_engine.getBaseURL()) + "/atom/" + encodedName, title ); 248 final SyndLink feedlink = createLink( "service.feed", HttpUtil.getAbsoluteUrl(request, m_engine.getBaseURL()) + "/rss.jsp?page=" + encodedName, title ); 249 250 feed.getLinks().add( postlink ); 251 feed.getLinks().add( feedlink ); 252 feed.getLinks().add( editlink ); 253 } 254 255 return feed; 256 } 257 258 private SyndLink createLink( final String rel, final String href, final String title ) { 259 final SyndLink link = new SyndLinkImpl(); 260 link.setRel( rel ); 261 link.setTitle( title ); 262 link.setType( "application/x.atom+xml" ); 263 link.setHref( href ); 264 265 return link; 266 } 267 268 /** 269 * {@inheritDoc} 270 */ 271 @Override 272 public void doDelete( final HttpServletRequest request, final HttpServletResponse response ) { 273 LOG.debug( "Received HTTP DELETE" ); 274 } 275 276 /** 277 * {@inheritDoc} 278 */ 279 @Override 280 public void doPut( final HttpServletRequest request, final HttpServletResponse response ) { 281 LOG.debug( "Received HTTP PUT" ); 282 } 283 284}