/*
* Copyright (c) 2010-2017, b3log.org & hacpai.com
*
* Licensed 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.
*/
package org.b3log.solo.service;
import org.b3log.latke.Latkes;
import org.b3log.latke.ioc.inject.Inject;
import org.b3log.latke.logging.Level;
import org.b3log.latke.logging.Logger;
import org.b3log.latke.repository.RepositoryException;
import org.b3log.latke.service.annotation.Service;
import org.b3log.latke.util.Strings;
import org.b3log.solo.repository.ArticleRepository;
import org.b3log.solo.repository.PageRepository;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Permalink query service.
*
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.0.0.2, Mar 4, 2014
* @since 0.6.1
*/
@Service
public class PermalinkQueryService {
/**
* Logger.
*/
private static final Logger LOGGER = Logger.getLogger(PermalinkQueryService.class.getName());
/**
* Page repository.
*/
@Inject
private PageRepository pageRepository;
/**
* Article repository.
*/
@Inject
private ArticleRepository articleRepository;
/**
* Reserved permalinks.
*/
public static final String[] RESERVED_LINKS = new String[] {
"/", "/article", "/tags.html", "/tags", "/page", "/blog-articles-feed.do", "/tag-articles-feed.do", "/blog-articles-rss.do",
"/tag-articles-rss.do", "/get-random-articles.do", "/article-random-double-gen.do", "/captcha.do", "/kill-browser",
"/add-article-comment.do", "/add-article-from-symphony-comment.do", "/add-page-comment.do", "/get-article-content", "/sitemap.xml",
"/login", "/logout", "/forgot", "/get-article-content", "/admin-index.do", "/admin-article.do", "/admin-article-list.do",
"/admin-link-list.do", "/admin-preference.do", "/admin-file-list.do", "/admin-page-list.do", "/admin-others.do",
"/admin-draft-list.do", "/admin-user-list.do", "/admin-plugin-list.do", "/admin-main.do", "/admin-about.do", "/admin-label",
"/admin-about.do", "/rm-all-data.do", "/init", "/register.html"
};
/**
* Checks whether the specified article permalink matches the system generated format
* pattern ("/articles/yyyy/MM/dd/${articleId}.html").
*
* @param permalink the specified permalink
* @return {@code true} if matches, returns {@code false} otherwise
*/
public static boolean matchDefaultArticlePermalinkFormat(final String permalink) {
final Pattern pattern = Pattern.compile("/articles/\\d{4}/\\d{2}/\\d{2}/\\d+\\.html");
final Matcher matcher = pattern.matcher(permalink);
return matcher.matches();
}
/**
* Checks whether the specified page permalink matches the system generated format pattern ("/pages/${pageId}.html").
*
* @param permalink the specified permalink
* @return {@code true} if matches, returns {@code false} otherwise
*/
public static boolean matchDefaultPagePermalinkFormat(final String permalink) {
final Pattern pattern = Pattern.compile("/pages/\\d+\\.html");
final Matcher matcher = pattern.matcher(permalink);
return matcher.matches();
}
/**
* Checks whether the specified permalink is a {@link #invalidArticlePermalinkFormat(java.lang.String) invalid article
* permalink format} and {@link #invalidPagePermalinkFormat(java.lang.String) invalid page permalink format}.
*
* @param permalink the specified permalink
* @return {@code true} if invalid, returns {@code false} otherwise
*/
public static boolean invalidPermalinkFormat(final String permalink) {
return invalidArticlePermalinkFormat(permalink) && invalidPagePermalinkFormat(permalink);
}
/**
* Checks whether the specified article permalink is invalid on format.
*
* @param permalink the specified article permalink
* @return {@code true} if invalid, returns {@code false} otherwise
*/
public static boolean invalidArticlePermalinkFormat(final String permalink) {
if (Strings.isEmptyOrNull(permalink)) {
return true;
}
if (matchDefaultArticlePermalinkFormat(permalink)) {
return false;
}
return invalidUserDefinedPermalinkFormat(permalink);
}
/**
* Checks whether the specified page permalink is invalid on format.
*
* @param permalink the specified page permalink
* @return {@code true} if invalid, returns {@code false} otherwise
*/
public static boolean invalidPagePermalinkFormat(final String permalink) {
if (Strings.isEmptyOrNull(permalink)) {
return true;
}
if (matchDefaultPagePermalinkFormat(permalink)) {
return false;
}
return invalidUserDefinedPermalinkFormat(permalink);
}
/**
* Checks whether the specified user-defined permalink is invalid on format.
*
* @param permalink the specified user-defined permalink
* @return {@code true} if invalid, returns {@code false} otherwise
*/
private static boolean invalidUserDefinedPermalinkFormat(final String permalink) {
if (Strings.isEmptyOrNull(permalink)) {
return true;
}
if (isReservedLink(permalink)) {
return true;
}
if (Strings.isNumeric(permalink.substring(1))) {
// See issue 120 (http://code.google.com/p/b3log-solo/issues/detail?id=120#c4) for more details
return true;
}
int slashCnt = 0;
for (int i = 0; i < permalink.length(); i++) {
if ('/' == permalink.charAt(i)) {
slashCnt++;
}
if (slashCnt > 1) {
return true;
}
}
return !Strings.isURL(Latkes.getServer() + permalink);
}
/**
* Determines whether the specified request URI is a reserved link.
*
* <p>
* A URI starts with one of {@link PermalinkQueryService#RESERVED_LINKS reserved links}
* will be treated as reserved link.
* </p>
*
* @param requestURI the specified request URI
* @return {@code true} if it is a reserved link, returns {@code false} otherwise
*/
private static boolean isReservedLink(final String requestURI) {
for (int i = 0; i < RESERVED_LINKS.length; i++) {
final String reservedLink = RESERVED_LINKS[i];
if (reservedLink.startsWith(requestURI)) {
return true;
}
}
return false;
}
/**
* Determines whether the specified permalink exists.
*
* @param permalink the specified permalink
* @return {@code true} if exists, returns {@code false} otherwise
*/
public boolean exist(final String permalink) {
try {
return isReservedLink(permalink) || null != articleRepository.getByPermalink(permalink)
|| null != pageRepository.getByPermalink(permalink) || permalink.endsWith(".ftl");
} catch (final RepositoryException e) {
LOGGER.log(Level.ERROR, "Determines whether the permalink[" + permalink + "] exists failed, returns true", e);
return true;
}
}
/**
* Sets the article repository with the specified article repository.
*
* @param articleRepository the specified article repository
*/
public void setArticleRepository(final ArticleRepository articleRepository) {
this.articleRepository = articleRepository;
}
/**
* Set the page repository with the specified page repository.
*
* @param pageRepository the specified page repository
*/
public void setPageRepository(final PageRepository pageRepository) {
this.pageRepository = pageRepository;
}
}