All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.anotheria.anosite.photoserver.presentation.delivery.DeliveryServlet Maven / Gradle / Ivy

The newest version!
package net.anotheria.anosite.photoserver.presentation.delivery;

import net.anotheria.anoplass.api.APIFinder;
import net.anotheria.anosite.photoserver.api.access.AccessAPI;
import net.anotheria.anosite.photoserver.api.access.AccessParameter;
import net.anotheria.anosite.photoserver.api.photo.PhotoAO;
import net.anotheria.anosite.photoserver.api.photo.PhotoAPI;
import net.anotheria.anosite.photoserver.api.photo.PhotoAPIConfig;
import net.anotheria.anosite.photoserver.api.photo.PhotoAPIException;
import net.anotheria.anosite.photoserver.api.photo.PhotoNotFoundPhotoAPIException;
import net.anotheria.anosite.photoserver.presentation.shared.BaseServlet;
import net.anotheria.anosite.photoserver.shared.CroppingType;
import net.anotheria.anosite.photoserver.shared.IdCrypter;
import net.anotheria.anosite.photoserver.shared.ModifyPhotoSettings;
import net.anotheria.anosite.photoserver.shared.ResizeType;
import net.anotheria.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Servlet for delivering photos by request.
 *
 * @author another
 * @version $Id: $Id
 */
public class DeliveryServlet extends BaseServlet {

	/**
	 * Full access cookie name.
	 */
	public static final String FULL_ACCESS_COOKIE_NAME = "ps_fa";
	/**
	 * {@link Logger} instance.
	 */
	private static final Logger LOGGER = LoggerFactory.getLogger(DeliveryServlet.class);

	// Parameter names and other constants
	/**
	 * Basic serialVersionUID variable.
	 */
	private static final long serialVersionUID = -3250585990874211552L;
	/**
	 * Internal server error message.
	 */
	private static final String INTERNAL_SERVER_ERROR_MSG = "Internal server error";
	/**
	 * Request parameter name for preview flag.
	 */
	private static final String PARAM_PREVIEW = "preview";
	/**
	 * Request parameter name for blur flag.
	 */
	private static final String PARAM_BLUR = "blur";
	/**
	 * Parameter name for access token.
	 */
	private static final String PARAM_ACCESS_TOKEN = "at";
    /**
     * Parameter name for cropping type.
     */
    private static final String PARAM_CROPPING_TYPE = "ct";
    /**
     * Parameter name for source.
     */
    private static final String PARAM_SOURCE= "s";
	/**
	 * Pattern for single size parameter - number.
	 */
	private static final Pattern sizePattern = Pattern.compile("\\d+|-1");
	/**
	 * Pattern for bounding area sizes - two numbers comma separated.
	 */
	private static final Pattern boundingAreaPattern = Pattern.compile("(\\d+),(\\d+)|-1");
	// APIs
	/**
	 * {@link AccessAPI} instance.
	 */
	private static final AccessAPI ACCESS_API = APIFinder.findAPI(AccessAPI.class);
	/**
	 * {@link PhotoAPI} instance.
	 */
	private static final PhotoAPI photoAPI = APIFinder.findAPI(PhotoAPI.class);
	/**
	 * {@link PhotoAPIConfig} instance.
	 */
	private static final PhotoAPIConfig photoAPIConfig = PhotoAPIConfig.getInstance();

	/**
	 * Sets appropriate HTTP status and other response info if photo can't be found.
	 *
	 * @param response
	 * 		{@link HttpServletResponse}
	 */
	private static void responseSetNotFound(final HttpServletResponse response) {
		response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY);
		response.setHeader("Location", DeliveryConfig.getInstance().getPhotoNotFoundLink());
	}

	/**
	 * Writes debug messages.
	 *
	 * @param messages
	 * 		messages to be written to log
	 */
	private static void debug(String... messages) {
		if (messages == null || messages.length == 0)
			return;

		if (LOGGER.isDebugEnabled())
			for (String message : messages)
				LOGGER.debug(message);
	}

	/** {@inheritDoc} */
	@Override
	protected void moskitoDoGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		String[] params = parsePathParameters(req);
		debug("Params[" + Arrays.toString(params) + "].");

		// checking encoded photo id parameter
		if (params == null || params.length < 1 || StringUtils.isEmpty(params[0])) {
			String message = "Wrong parameters[" + Arrays.toString(params) + "].";
			LOGGER.info("doGet(req, resp) fail. " + message);
			responseSetNotFound(resp);
			return;
		}

		final String blurredPathParameter = params[1];
		final String rawPhotoId = params[2];

		long photoId;
		try {
			photoId = IdCrypter.decodeToLong(rawPhotoId); // decode encoded photo id
		} catch (RuntimeException re) {
			String message = "Wrong photo id[" + rawPhotoId + "] parameter.";
			LOGGER.info("doGet(req, resp) fail. " + message);
			responseSetNotFound(resp);
			return;
		}

		final ModifyPhotoSettings modifyPhotoSettings = new ModifyPhotoSettings();

		// getting size for requested photo
		if (params.length > 1) {
			boolean sizeDataValid = buildSizeParametersAndValidate(modifyPhotoSettings, params[3]);
			if(!sizeDataValid){
				String message = "Wrong size[" + params[3] + "] parameter.";
				LOGGER.info("doGet(req, resp) fail. " + message);

				responseSetNotFound(resp);
				return;
			}

			// check is requested size is allowed
			if (!photoAPIConfig.isIgnoreAllowedSizes() && !photoAPIConfig.isAllowedSize(modifyPhotoSettings.getSize()) ) {
				LOGGER.info("doGet(req, resp) fail. " + "Requested size[" + params[3] + "] not allowed.");
				responseSetNotFound(resp);
				return;
			}
		}

		PhotoAO photo = getPhoto(photoId);
		if (photo == null) {
			responseSetNotFound(resp);
			return;
		}

        int croppingType = DeliveryConfig.getInstance().getCroppingType().getValue();
        if (! StringUtils.isEmpty(req.getParameter(PARAM_CROPPING_TYPE))){
            try {
                croppingType = Integer.parseInt(req.getParameter(PARAM_CROPPING_TYPE));

                if(CroppingType.valueOf(croppingType)==null){
                    responseSetNotFound(resp);
                    return;
                }
            } catch (NumberFormatException e){
                responseSetNotFound(resp);
                return;
            }
        }

		boolean cropped = req.getParameter(PARAM_PREVIEW) != null;
		boolean resized = modifyPhotoSettings.isResized();
		boolean blurred = req.getParameter(PARAM_BLUR) != null || (photo.isBlurred() && blurredPathParameter.equals("b"));

        Map optionalParameters = new HashMap<>();
        optionalParameters.put(AccessParameter.SOURCE, req.getParameter(PARAM_SOURCE));
        optionalParameters.put(AccessParameter.BLUR, req.getParameter(PARAM_BLUR));
        optionalParameters.put(AccessParameter.PREVIEW, req.getParameter(PARAM_PREVIEW));
        optionalParameters.put(AccessParameter.ACCESS_TOKEN, req.getParameter(PARAM_ACCESS_TOKEN));
        optionalParameters.put(AccessParameter.ACCESS_COOKIE_NAME, req.getParameter(FULL_ACCESS_COOKIE_NAME));

        // get full access cookie value
        String fullAccessCookieValue = DeliveryConfig.getInstance().getRestrictionBypassCookie();
        // check whether current user has full access granted by cookie
        if (fullAccessCookieValue == null || ! fullAccessCookieValue.equals(getCookieValue(req, FULL_ACCESS_COOKIE_NAME)))
            switch (ACCESS_API.isViewAllowed(photo, optionalParameters)){
                case VIEW_ALLOWED:
                    break;
                case BLURRED_VIEW_ALLOWED:
                    blurred &= true;
                    break;
                case VIEW_DENIED: // same as default
                default:
                    resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
                    return;
            }

		try {
			// check is delivering for original photos enabled
			if (!cropped && !DeliveryConfig.getInstance().isOriginalPhotosAccessible()) {
				debug("Delivering of original photo is denied. Original photo id: " + photo.getId());
				responseSetNotFound(resp);
				return;
			}


			// delivering original photo
			if (!cropped && !resized && !blurred) {
				InputStream photoInputStream = photoAPI.getPhotoContent(photo);
				debug("Returning original photo: " + photo.getFilePath());
				writeImageHeaders(resp);
				stream(resp, photoInputStream);
				return;
			}

			writeImageHeaders(resp);
			stream(resp, photoAPI.getCachedPhotoContent(photo, modifyPhotoSettings, cropped, croppingType, blurred));
		} catch (Exception e) {
			String message = "doGet(req, resp) fail. " + INTERNAL_SERVER_ERROR_MSG;
			LOGGER.warn(message, e);
			responseSetNotFound(resp);
		}
	}

	/**
	 * Validate incoming size data and populate photo settings holder with it.
	 *
	 * @param modifyPhotoSettings {@link ModifyPhotoSettings}
	 * @param params              size data - single number or two comma separated numbers
	 * @return {@code true} - if incoming size data is valid, {@code false} - otherwise
	 */
	private boolean buildSizeParametersAndValidate(final ModifyPhotoSettings modifyPhotoSettings, final String params) {
		if (StringUtils.isEmpty(params))
			return false;

		Matcher m = sizePattern.matcher(params);
		if (m.matches()) {
			modifyPhotoSettings.setSize(Integer.parseInt(params));
			modifyPhotoSettings.setResizeType(ResizeType.SIZE);
			return true;
		}

		m = boundingAreaPattern.matcher(params);
		if (m.matches()) {
			modifyPhotoSettings.setBoundaryWidth(Integer.parseInt(m.group(1)));
			modifyPhotoSettings.setBoundaryHeight(Integer.parseInt(m.group(2)));
			modifyPhotoSettings.setResizeType(ResizeType.BOUNDING_AREA);
			return true;
		}

		return false;
	}

	/**
	 * Returns {@link PhotoAO} by given photo id, or {@code null} if photo can't be found.
	 *
	 * @param photoId
	 * 		id of the photo
	 * @return {@link PhotoAO}, or {@code null}
	 */
	private PhotoAO getPhoto(final long photoId) {
		try {
			return photoAPI.getPhoto(photoId);
		} catch (PhotoNotFoundPhotoAPIException e) {
			LOGGER.warn("doGet(req, resp) fail. Photo[" + photoId + "] not found.");
		} catch (PhotoAPIException e) {
			LOGGER.warn("doGet(req, resp) fail. " + INTERNAL_SERVER_ERROR_MSG);
		}

		return null;
	}

	/**
	 * 

getCookieByName.

* * @param req a {@link jakarta.servlet.http.HttpServletRequest} object. * @param name a {@link java.lang.String} object. * @return a {@link jakarta.servlet.http.Cookie} object. */ public static Cookie getCookieByName(HttpServletRequest req, String name) { Cookie result = null; Cookie[] cookies = req.getCookies(); if (cookies != null){ for (Cookie cookie : cookies) { if (name.equals(cookie.getName())) { return cookie; } } } return result; } /** *

getCookieValue.

* * @param req a {@link jakarta.servlet.http.HttpServletRequest} object. * @param name a {@link java.lang.String} object. * @return a {@link java.lang.String} object. */ public static String getCookieValue(HttpServletRequest req, String name) { Cookie cookie = getCookieByName(req, name); if(cookie != null) return cookie.getValue(); return null; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy