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

com.aoindustries.website.framework.WebSiteRequest Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * aoweb-framework - Legacy servlet-based web framework, superfast and capable but tedious to use.
 * Copyright (C) 2000-2013, 2014, 2015, 2016  AO Industries, Inc.
 *     [email protected]
 *     7262 Bull Pen Cir
 *     Mobile, AL 36695
 *
 * This file is part of aoweb-framework.
 *
 * aoweb-framework is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * aoweb-framework is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with aoweb-framework.  If not, see .
 */
package com.aoindustries.website.framework;

import com.aoindustries.encoding.ChainWriter;
import com.aoindustries.io.FileUtils;
import com.aoindustries.security.LoginException;
import com.aoindustries.util.SortedArrayList;
import com.aoindustries.util.StringUtility;
import com.aoindustries.util.WrappedException;
import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.FileRenamePolicy;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.logging.Level;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;

/**
 * A WebSiteSettings contains all the values that a user may customize while they view the web site.
 *
 * @author  AO Industries, Inc.
 */
public class WebSiteRequest extends HttpServletRequestWrapper implements FileRenamePolicy {

	/**
	 * Gets the random number generator used for this request.
	 */
	public Random getRandom() throws IOException {
		return ErrorReportingServlet.getRandom();
	}

	private static String getExtension(String filename) {
		int pos=filename.lastIndexOf('.');
		if(pos==-1 || pos==(filename.length()-1)) return filename;
		else return filename.substring(pos+1);
	}

	private static final Object mimeTypeLock=new Object();
	private static Map mimeTypes;
	private static String getContentType(MultipartRequest mreq, String filename) throws IOException {
		synchronized(mimeTypeLock) {
			if(mimeTypes==null) {
				Map newMap=new HashMap<>();
				try (BufferedReader in = new BufferedReader(new InputStreamReader(WebSiteRequest.class.getResourceAsStream("mime.types")))) {
					String line;
					while((line=in.readLine())!=null) {
						if(line.length()>0) {
							if(line.charAt(0)!='#') {
								String[] words=StringUtility.splitString(line);
								if(words.length>0) {
									String type=words[0];
									for(int c=1;c uploadedFiles=new HashMap<>();
	private long getNextID() throws IOException {
		Random random=getRandom();
		synchronized(uploadedFiles) {
			while(true) {
				long id=random.nextLong()&0x7fffffffffffffffL;
				Long ID=id;
				if(!uploadedFiles.containsKey(ID)) {
					uploadedFiles.put(ID, null);
					return id;
				}
			}
		}
	}

	private static Thread uploadedFileCleanup;
	private static void addUploadedFile(UploadedFile uf, final ServletContext servletContext, final LoggerAccessor loggerAccessor) {
		synchronized(uploadedFiles) {
			uploadedFiles.put(uf.getID(), uf);

			if(uploadedFileCleanup==null) {
				uploadedFileCleanup=new Thread() {
					@Override
					public void run() {
						while(true) {
							try {
								while(true) {
									sleep(10*60*1000);

									// Remove the expired entries
									synchronized(uploadedFiles) {
										Iterator I=uploadedFiles.keySet().iterator();
										while(I.hasNext()) {
											Long ID=I.next();
											UploadedFile uf=uploadedFiles.get(ID);
											if(uf==null) {
												I.remove();
											} else {
												long timeSince=System.currentTimeMillis()-uf.getLastAccessed();
												if(timeSince<0 || timeSince>=((long)60*60*1000)) {
													File file=uf.getStorageFile();
													if(file.exists()) {
														try {
															FileUtils.delete(file);
														} catch(IOException e) {
															loggerAccessor.getLogger(
																servletContext,
																getClass().getName()
															).log(
																Level.SEVERE,
																"file.getPath()="+file.getPath(),
																e
															);
														}
													}
													I.remove();
												}
											}
										}
										// Delete the files that do not have an uploadedFile entry and are at least two hours old
										File dir=WebSiteFrameworkConfiguration.getFileUploadDirectory();
										String[] list=dir.list();
										if(list!=null) {
											for(String filename : list) {
												File file = new File(dir, filename);
												long fileAge=System.currentTimeMillis()-file.lastModified();
												if(fileAge<((long)-2*60*60*1000) || fileAge>((long)2*60*60*1000)) {
													boolean found=false;
													I=uploadedFiles.keySet().iterator();
													while(I.hasNext()) {
														UploadedFile uf=uploadedFiles.get(I.next());
														if(uf.getStorageFile().getAbsolutePath().equals(file.getAbsolutePath())) {
															found=true;
															break;
														}
													}
													if(!found) {
														try {
															FileUtils.delete(file);
														} catch(IOException e) {
															loggerAccessor.getLogger(
																servletContext,
																getClass().getName()
															).log(
																Level.SEVERE,
																"file.getPath()="+file.getPath(),
																e
															);
														}
													}
												}
											}
										}
									}
								}
							} catch(ThreadDeath TD) {
								throw TD;
							} catch(RuntimeException | InterruptedException | IOException T) {
								loggerAccessor.getLogger(servletContext, getClass().getName()).log(Level.SEVERE, null, T);
								try {
									sleep(60*1000);
								} catch(InterruptedException err) {
									loggerAccessor.getLogger(servletContext, getClass().getName()).log(Level.WARNING, null, err);
								}
							}
						}
					}
				};
				uploadedFileCleanup.start();
			}
		}
	}

	final protected WebPage sourcePage;
	final private HttpServletRequest req;
	private MultipartRequest mreq;
	private List reqUploadedFiles;

	private boolean isLynx;
	private boolean isLynxDone;

	private boolean isBlackBerry;
	private boolean isBlackBerryDone;

	private boolean isLinux;
	private boolean isLinuxDone;

	public WebSiteRequest(WebPage sourcePage, HttpServletRequest req) throws IOException, SQLException {
		super(req);
		this.sourcePage=sourcePage;
		this.req=req;
		String contentType=req.getHeader("Content-Type");
		if (contentType!=null && contentType.length()>=19 && contentType.substring(0,19).equals("multipart/form-data")) {
			boolean keepFiles=false;
			try {
				mreq = new MultipartRequest(req, WebSiteFrameworkConfiguration.getFileUploadDirectory().getPath(), WebSiteFrameworkConfiguration.getMaxFileUploadSize(), this);
				try {
					// Determine the authentication info
					WebSiteUser user=getWebSiteUser(null);
					if(user!=null) {
						keepFiles=true;
						// Create an UploadedFile for each file in the MultipartRequest
						reqUploadedFiles=new ArrayList<>();
						@SuppressWarnings("unchecked")
						Enumeration E=mreq.getFileNames();
						while(E.hasMoreElements()) {
							String filename=E.nextElement();
							File file=mreq.getFile(filename);
							if(file!=null) {
								file.deleteOnExit();
								UploadedFile uf=new UploadedFile(
									mreq.getOriginalFileName(filename),
									file,
									user,
									getContentType(mreq, filename)
								);
								addUploadedFile(uf, sourcePage.getServletContext(), sourcePage.getLoggerAccessor());
								reqUploadedFiles.add(uf);
							}
						}
					}
				} catch(LoginException err) {
					// Ignore the error, just allow the files to be cleaned up because keepFiles is still false
				}
			} finally {
				if(!keepFiles && mreq!=null) {
					@SuppressWarnings("unchecked")
					Enumeration E=mreq.getFileNames();
					while(E.hasMoreElements()) {
						String filename=E.nextElement();
						File file=mreq.getFile(filename);
						if(file!=null && file.exists()) {
							FileUtils.delete(file);
						}
					}
				}
			}
		} else this.mreq = null;
	}

	/**
	 * Appends the parameters to a URL.
	 * Parameters should already be URL encoded but not XML encoded.
	 */
	protected static boolean appendParams(StringBuilder SB, Object optParam, List finishedParams, boolean alreadyAppended) {
		if (optParam != null) {
			if (optParam instanceof String) {
				List nameValuePairs=StringUtility.splitString((String)optParam, '&');
				int len=nameValuePairs.size();
				for(int i=0;i clazz=Class.forName(classname).asSubclass(WebPage.class);
			return getURL(clazz, params);
		} catch(ClassNotFoundException err) {
			throw new IOException("Unable to load class: "+classname, err);
		}
	}

	/**
	 * Gets the context-relative URL, optionally with the settings embedded.
	 * Parameters should already be URL encoded but not XML encoded.
	 * 
	 * @param  url  the context-relative URL
	 */
	public String getURL(String url, Object optParam, boolean keepSettings) throws IOException {
		StringBuilder SB=new StringBuilder();
		SB.append(url);
		List finishedParams=new SortedArrayList<>();
		boolean alreadyAppended=appendParams(SB, optParam, finishedParams, false);
		if(keepSettings) appendSettings(finishedParams, alreadyAppended, SB);
		return SB.toString();
	}

	protected boolean appendSettings(List finishedParams, boolean alreadyAppended, StringBuilder SB) {
		boolean searchEngine="true".equals(getParameter("search_engine"));
		if(searchEngine) alreadyAppended=appendParams(SB, new String[] {"search_engine", "true"}, finishedParams, alreadyAppended);
		return alreadyAppended;
	}

	@Override
	public String getParameter(String name) {
		if(mreq==null) return req.getParameter(name);
		else {
			String param=mreq.getParameter(name);
			if(param==null) param=req.getParameter(name);
			return param;
		}
	}

	@Override
	@SuppressWarnings("unchecked")
	public Enumeration getParameterNames() {
		if (mreq==null) return (Enumeration)req.getParameterNames();
		return (Enumeration)mreq.getParameterNames();
	}

	@Override
	public String[] getParameterValues(String name) {
		if (mreq==null) return req.getParameterValues(name);
		return mreq.getParameterValues(name);
	}

	/**
	 * Gets the context-relative URL to a web page.
	 */
	public String getURL(WebPage page) throws IOException, SQLException {
		return getURL(page, (Object)null);
	}

	/**
	 * Gets the context-relative URL to a web page.
	 * Parameters should already be URL encoded but not yet XML encoded.
	 */
	public String getURL(WebPage page, Object optParam) throws IOException, SQLException {
		List finishedParams=new SortedArrayList<>();
		StringBuilder SB = new StringBuilder();
		SB.append(page.getURLPath());
		boolean alreadyAppended=appendParams(SB, optParam, finishedParams, false);
		alreadyAppended=appendParams(SB, page.getURLParams(this), finishedParams, alreadyAppended);

		/*alreadyAppended=*/appendSettings(finishedParams, alreadyAppended, SB);

		return SB.toString();
	}

	/**
	 * Gets the context-relative URL to a web page.
	 * Parameters should already be URL encoded but not yet XML encoded.
	 */
	public String getURL(Class clazz, Object param) throws IOException, SQLException {
		WebPage page=WebPage.getWebPage(sourcePage.getServletContext(), clazz, param);
		return getURL(page, param);
	}

	public String getURL(Class clazz) throws IOException, SQLException {
		return getURL(clazz, (Object)null);
	}

	/**
	 * Gets the URL String with the given parameters embedded, keeping the current settings.
	 *
	 * @param  url            the context-relative URL, with a beginning slash
	 * @param  optParam       any number of additional parameters.  This parameter can accept several types of
	 *                        objects.  The following is a list of supported objects and a brief description of its
	 *                        behavior.
	 *                        Parameters should already be URL encoded but not yet XML encoded.
	 *                        
    *
  • * String - appended to the end of the parameters, assumed to be in the * format name=value *
  • *
  • * String[] - name and value pairs, the first element of each pair is the * name, the second is the value *
  • *
* @exception IllegalArgumentException if optParam is not a supported object */ public String getURL(String url, Object optParam) throws IOException { return getURL(url, optParam, true); } /** * Determines if the request is for a Lynx browser */ public boolean isLynx() { if(!isLynxDone) { String agent = req.getHeader("user-agent"); isLynx=agent != null && agent.toLowerCase(Locale.ROOT).contains("lynx"); isLynxDone=true; } return isLynx; } /** * Determines if the request is for a BlackBerry browser */ public boolean isBlackBerry() { if(!isBlackBerryDone) { String agent = req.getHeader("user-agent"); isBlackBerry=agent != null && agent.startsWith("BlackBerry"); isBlackBerryDone=true; } return isBlackBerry; } /** * Determines if the request is for a Linux browser */ public boolean isLinux() { if(!isLinuxDone) { String agent = req.getHeader("user-agent"); isLinux=agent == null || agent.toLowerCase(Locale.ROOT).contains("linux"); isLinuxDone=true; } return isLinux; } @Override public boolean isSecure() { return req.isSecure() || req.getServerPort()==443 || req.getRequestURI().contains("/https/"); } /** * Prints the hidden variables that contain all of the current settings. */ public void printFormFields(ChainWriter out, int indent) throws IOException { if("true".equals(req.getParameter("search_engine"))) printHiddenField(out, indent, "search_engine", "true"); } /** * Prints the hidden variables that contain all of the current settings. */ protected static void printHiddenField(ChainWriter out, int indent, String name, String value) throws IOException { for(int c=0;c\n"); } public List getUploadedFiles() { if(reqUploadedFiles==null) Collections.emptyList(); return Collections.unmodifiableList(reqUploadedFiles); } /** * Gets a file that was uploaded given its ID. The authentication * credentials for this request must match those of the provided ID. * * @return the owner of the object * @return the UploadedFile or null if not found * * @exception SecurityException if the ID is not assigned to the person logged in */ public static UploadedFile getUploadedFile(WebSiteUser owner, long id, ServletContext context, LoggerAccessor loggerAccessor) throws SecurityException { synchronized(uploadedFiles) { UploadedFile uf=uploadedFiles.get(Long.valueOf(id)); if(uf!=null) { if(uf.getOwner().equals(owner)) return uf; else { loggerAccessor.getLogger( context, WebSiteRequest.class.getName() ).log( Level.SEVERE, "UploadedFile found, but owner doesn''t match: uf.getOwner()=\"{0}\", owner=\"{1}\".", new Object[] { uf.getOwner(), owner } ); } } return null; } } @Override public File rename(File file) { try { while(true) { File newFile=new File(WebSiteFrameworkConfiguration.getFileUploadDirectory(), String.valueOf(getNextID())); if(!newFile.exists()) return newFile; } } catch(IOException err) { throw new WrappedException(err, new Object[] {"file.getPath()="+file.getPath()}); } } /** * Gets the person who is logged in or null if no login is performed for this request. * * @exception LoginException if an invalid login attempt is made or the user credentials are not found */ public WebSiteUser getWebSiteUser(HttpServletResponse resp) throws IOException, SQLException, LoginException { return null; } /** * Determines if the user is currently logged in. */ public boolean isLoggedIn() throws IOException, SQLException { try { return getWebSiteUser(null)!=null; } catch(LoginException err) { return false; } } /** * Logs out the current user or does nothing if not logged in. */ public void logout(HttpServletResponse resp) { } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy