Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@

### Bug fixes

- **Weblogs no longer advertise XML-RPC while it is switched off.** With
`webservices.enableXmlRpc` off, each weblog page still linked to an RSD
document listing the Blogger and MetaWeblog endpoints, which then answered
404. The link now appears, and the RSD document answers, only while both
that setting and the weblog's client support are on. Refused XML-RPC
requests are logged at debug level instead of as a warning each.
- **A bad date pattern in a theme no longer blanks the page.**
`$utils.formatDate` logs a warning and uses the locale's standard date and
time format. Older themes often pass a message key that no longer exists,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {

if (!WebloggerRuntimeConfig.getBooleanProperty("webservices.enableXmlRpc")) {
LOG.warn("XML-RPC service is disabled; rejecting request");
LOG.debug("XML-RPC service is disabled; rejecting request");
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
httpResponse.setContentType("text/plain;charset=UTF-8");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ public String getDefaultAnalyticsTrackingCode() {
return getProperty("analytics.default.tracking.code");
}

/** Whether the site answers the Blogger and MetaWeblog APIs at all. */
public boolean getXmlRpcEnabled() {
return getBooleanProperty("webservices.enableXmlRpc");
}

public boolean getAnalyticsOverrideAllowed() {
return getBooleanProperty("analytics.code.override.allowed");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ public void doGet(HttpServletRequest request, HttpServletResponse response)
return;
}

// The RSD describes the XML-RPC endpoint, so there is nothing to
// describe while the site or the weblog doesn't answer it
if (!describesXmlRpc(weblog)) {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}

// Respond with 304 Not Modified if it is not modified.
long lastModified = System.currentTimeMillis();
if (weblog.getLastModified() != null) {
Expand Down Expand Up @@ -164,4 +171,10 @@ public void doGet(HttpServletRequest request, HttpServletResponse response)
log.debug("Exiting");
}


/** True while both the site and this weblog answer the Blogger and MetaWeblog APIs. */
static boolean describesXmlRpc(Weblog weblog) {
return WebloggerRuntimeConfig.getBooleanProperty("webservices.enableXmlRpc")
&& Boolean.TRUE.equals(weblog.getEnableBloggerApi());
}
}
3 changes: 2 additions & 1 deletion app/src/main/webapp/WEB-INF/velocity/weblog.vm
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ Parse and include page template from weblog.
Show RSS, Atom and RSD auto-discovery links as HTML link elements.
*#
#macro(showAutodiscoveryLinks $weblog)
#if ($weblog.enableBloggerApi)
## only advertise the RSD while the site and the weblog both answer XML-RPC
#if ($weblog.enableBloggerApi && $config.xmlRpcEnabled)
<link rel="EditURI" type="application/rsd+xml" title="RSD" href="$url.rsd"/>
#end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. The ASF licenses this file to You
* under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. For additional information regarding
* copyright in this work, please see the NOTICE file in the top level
* directory of this distribution.
*/

package org.apache.roller.weblogger.ui.rendering.servlets;

import java.io.StringWriter;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Properties;

import org.apache.roller.weblogger.config.WebloggerRuntimeConfig;
import org.apache.roller.weblogger.pojos.Weblog;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;

/**
* The RSD document and the link to it describe the Blogger and MetaWeblog
* endpoint, so they must only appear while the site and the weblog both
* answer it.
*/
public class RSDServletTest {

private static Weblog weblog(Boolean clientSupport) {
Weblog weblog = mock(Weblog.class);
when(weblog.getEnableBloggerApi()).thenReturn(clientSupport);
return weblog;
}

private static boolean describes(boolean siteEnabled, Boolean weblogEnabled) {
try (MockedStatic<WebloggerRuntimeConfig> config = mockStatic(WebloggerRuntimeConfig.class)) {
config.when(() -> WebloggerRuntimeConfig.getBooleanProperty("webservices.enableXmlRpc"))
.thenReturn(siteEnabled);
return RSDServlet.describesXmlRpc(weblog(weblogEnabled));
}
}

@Test
public void describesXmlRpcOnlyWhenTheSiteAndTheWeblogAnswerIt() {
assertTrue(describes(true, true));
assertFalse(describes(false, true));
assertFalse(describes(true, false));
assertFalse(describes(true, null));
assertFalse(describes(false, false));
}

/** Stands in for ConfigModel. */
public static class StubConfig {
private final boolean xmlRpcEnabled;

StubConfig(boolean xmlRpcEnabled) {
this.xmlRpcEnabled = xmlRpcEnabled;
}

public boolean getXmlRpcEnabled() {
return xmlRpcEnabled;
}
}

/** Stands in for the URL model; only the RSD link matters here. */
public static class StubUrl {
public String getRsd() {
return "http://example.org/roller/blog/rsd";
}
}

/**
* The app module's directory, found from where this class was compiled to
* (target/test-classes), so the templates are found whatever the working
* directory is, as when the test runs from an IDE.
*/
private static Path moduleDir() {
try {
Path classes = Paths.get(RSDServletTest.class.getProtectionDomain()
.getCodeSource().getLocation().toURI());
return classes.getParent().getParent();
} catch (URISyntaxException e) {
throw new IllegalStateException(e);
}
}

private static String autodiscovery(boolean siteEnabled, boolean weblogEnabled) {
Properties props = new Properties();
props.setProperty("resource.loaders", "file");
props.setProperty("resource.loader.file.class",
"org.apache.velocity.runtime.resource.loader.FileResourceLoader");
props.setProperty("resource.loader.file.path",
moduleDir().resolve("src/main/webapp/WEB-INF/velocity").toString());
VelocityEngine engine = new VelocityEngine();
engine.init(props);

VelocityContext ctx = new VelocityContext();
ctx.put("weblog", weblog(weblogEnabled));
ctx.put("config", new StubConfig(siteEnabled));
ctx.put("url", new StubUrl());
StringWriter out = new StringWriter();
engine.evaluate(ctx, out, "test", "#parse('weblog.vm')#showAutodiscoveryLinks($weblog)");
return out.toString();
}

@Test
public void autodiscoveryLinksToTheRsdOnlyWhenItWouldBeServed() {
String rsdLink = "rel=\"EditURI\" type=\"application/rsd+xml\" title=\"RSD\" "
+ "href=\"http://example.org/roller/blog/rsd\"";
assertTrue(autodiscovery(true, true).contains(rsdLink), autodiscovery(true, true));
assertFalse(autodiscovery(false, true).contains("EditURI"));
assertFalse(autodiscovery(true, false).contains("EditURI"));
assertEquals(autodiscovery(false, true), autodiscovery(true, false));
}
}
Loading