/* WMRequest
*
* Created on 2005/10/18 14:00:00
*
* Copyright (C) 2005 Internet Archive.
*
* This file is part of the Wayback Machine (crawler.archive.org).
*
* Wayback Machine is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* any later version.
*
* Wayback Machine 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 Public License for more details.
*
* You should have received a copy of the GNU Lesser Public License
* along with Wayback Machine; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.archive.wayback.core;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.ResourceBundle;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.httpclient.URIException;
import org.archive.net.UURI;
import org.archive.net.UURIFactory;
import org.archive.wayback.WaybackConstants;
import org.archive.wayback.requestparser.OpenSearchRequestParser;
import org.archive.wayback.util.ObjectFilter;
import org.archive.wayback.util.StringFormatter;
import org.archive.wayback.webapp.AccessPoint;
/**
* Abstraction of all the data associated with a users request to the Wayback
* Machine.
*
* @author Brad Tofel
* @version $Date: 2007-11-28 00:43:02 +0000 (Wed, 28 Nov 2007) $, $Revision: 2079 $
*/
public class WaybackRequest {
private int resultsPerPage = 10;
private int pageNum = 1;
private String contextPrefix = null;
private String serverPrefix = null;
private AccessPoint context = null;
private ObjectFilter<SearchResult> exclusionFilter = null;
private HashMap<String,String> filters = new HashMap<String,String>();
private StringFormatter formatter = null;
private static String UI_RESOURCE_BUNDLE_NAME = "WaybackUI";
private final static String standardHeaders[] = {
WaybackConstants.REQUEST_REFERER_URL,
WaybackConstants.REQUEST_REMOTE_ADDRESS,
WaybackConstants.REQUEST_WAYBACK_HOSTNAME,
WaybackConstants.REQUEST_WAYBACK_PORT,
WaybackConstants.REQUEST_WAYBACK_CONTEXT,
WaybackConstants.REQUEST_AUTH_TYPE,
WaybackConstants.REQUEST_REMOTE_USER,
WaybackConstants.REQUEST_LOCALE_LANG };
/**
* Constructor, possibly/probably this should BE a Properties, instead of
* HAVEing a Properties...
*/
public WaybackRequest() {
super();
}
/**
* @return true if REQUEST_TYPE is set, and is set to REQUEST_REPLAY_QUERY
*/
public boolean isReplayRequest() {
String type = get(WaybackConstants.REQUEST_TYPE);
if(type != null && type.equals(WaybackConstants.REQUEST_REPLAY_QUERY)) {
return true;
}
return false;
}
/**
* @return true if true if REQUEST_TYPE is not set, or is set to a value
* other than REQUEST_REPLAY_QUERY
*/
public boolean isQueryRequest() {
return !isReplayRequest();
}
/**
* @return Returns the pageNum.
*/
public int getPageNum() {
return pageNum;
}
/**
* @param pageNum
* The pageNum to set.
*/
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
/**
* @return Returns the resultsPerPage.
*/
public int getResultsPerPage() {
return resultsPerPage;
}
/**
* @param resultsPerPage
* The resultsPerPage to set.
*/
public void setResultsPerPage(int resultsPerPage) {
this.resultsPerPage = resultsPerPage;
}
/**
* @param key
* @return boolean, true if the request contains key 'key'
*/
public boolean containsKey(String key) {
return filters.containsKey(key);
}
/**
* @param key
* @return String value for key 'key', or null if no value exists
*/
public String get(String key) {
return (String) filters.get(key);
}
/**
* @param key
* @param value
*/
public void put(String key, String value) {
filters.put(key, value);
}
private String emptyIfNull(String arg) {
if (arg == null) {
return "";
}
return arg;
}
/**
* Set the Locale for the request, which impacts UI Strings
* @param l
*/
public void setLocale(Locale l) {
ResourceBundle b = ResourceBundle.getBundle(UI_RESOURCE_BUNDLE_NAME,l);
formatter = new StringFormatter(b,l);
}
private String getUserLocale(HttpServletRequest httpRequest) {
Locale l = httpRequest.getLocale();
ResourceBundle b = ResourceBundle.getBundle(UI_RESOURCE_BUNDLE_NAME,
httpRequest.getLocale());
formatter = new StringFormatter(b,l);
return emptyIfNull(httpRequest.getLocale().getDisplayLanguage());
}
/**
* extract REFERER, remote IP and authorization information from the
* HttpServletRequest
*
* @param httpRequest
*/
private void extractHttpRequestInfo(HttpServletRequest httpRequest) {
// attempt to get the HTTP referer if present..
put(WaybackConstants.REQUEST_REFERER_URL, emptyIfNull(httpRequest
.getHeader("REFERER")));
put(WaybackConstants.REQUEST_REMOTE_ADDRESS, emptyIfNull(httpRequest
.getRemoteAddr()));
put(WaybackConstants.REQUEST_WAYBACK_HOSTNAME, emptyIfNull(httpRequest
.getLocalName()));
put(WaybackConstants.REQUEST_WAYBACK_PORT, String.valueOf(httpRequest
.getLocalPort()));
put(WaybackConstants.REQUEST_WAYBACK_CONTEXT, emptyIfNull(httpRequest
.getContextPath()));
put(WaybackConstants.REQUEST_AUTH_TYPE, emptyIfNull(httpRequest
.getAuthType()));
put(WaybackConstants.REQUEST_REMOTE_USER, emptyIfNull(httpRequest
.getRemoteUser()));
put(WaybackConstants.REQUEST_LOCALE_LANG,getUserLocale(httpRequest));
Cookie[] cookies = httpRequest.getCookies();
if(cookies != null) {
for(Cookie cookie : cookies) {
put(cookie.getName(),cookie.getValue());
}
}
}
/**
* @param prefix
*/
public void setServerPrefix(String prefix) {
serverPrefix = prefix;
}
/**
* @param prefix
* @return an absolute String URL that will point to the root of the
* server that is handling the request.
*/
public String getServerPrefix() {
if(serverPrefix == null) {
return "";
}
return serverPrefix;
}
/**
* @param prefix
*/
public void setContextPrefix(String prefix) {
contextPrefix = prefix;
}
/**
* Construct an absolute URL that points to the root of the context that
* recieved the request, including a trailing "/".
*
* @return String absolute URL pointing to the Context root where the
* request was revieved.
*/
public String getContextPrefix() {
if(contextPrefix == null) {
return "";
}
return contextPrefix;
}
/**
* attempt to fixup this WaybackRequest, mostly with respect to dates: if
* only "date" was specified, infer start and end dates from it. Also grab
* useful info from the HttpServletRequest, cookies, remote address, etc.
*
* @param httpRequest
*/
public void fixup(HttpServletRequest httpRequest) {
String startDate = get(WaybackConstants.REQUEST_START_DATE);
String endDate = get(WaybackConstants.REQUEST_END_DATE);
String exactDate = get(WaybackConstants.REQUEST_EXACT_DATE);
String partialDate = get(WaybackConstants.REQUEST_DATE);
if (partialDate == null) {
partialDate = "";
}
if (startDate == null || startDate.length() == 0) {
put(WaybackConstants.REQUEST_START_DATE, Timestamp
.padStartDateStr(partialDate));
} else if (startDate.length() < 14) {
put(WaybackConstants.REQUEST_START_DATE, Timestamp
.padStartDateStr(startDate));
}
if (endDate == null || endDate.length() == 0) {
put(WaybackConstants.REQUEST_END_DATE, Timestamp
.padEndDateStr(partialDate));
} else if (endDate.length() < 14) {
put(WaybackConstants.REQUEST_END_DATE, Timestamp
.padEndDateStr(endDate));
}
if (exactDate == null || exactDate.length() == 0) {
put(WaybackConstants.REQUEST_EXACT_DATE, Timestamp
.padEndDateStr(partialDate));
} else if (exactDate.length() < 14) {
put(WaybackConstants.REQUEST_EXACT_DATE, Timestamp
.padEndDateStr(exactDate));
}
extractHttpRequestInfo(httpRequest);
}
/**
* @return String hex-encoded GET CGI arguments which will duplicate this
* wayback request
*/
public String getQueryArguments() {
return getQueryArguments(pageNum);
}
/**
* @param pageNum
* @return String hex-encoded GET CGI arguments which will duplicate the
* same request, but for page 'pageNum' of the results
*/
public String getQueryArguments(int pageNum) {
int numPerPage = resultsPerPage;
StringBuffer queryString = new StringBuffer("");
Iterator<String> itr = filters.keySet().iterator();
while(itr.hasNext()) {
String key = itr.next();
boolean isStandard = false;
for(int i=0; i<standardHeaders.length; i++) {
if(standardHeaders[i].equals(key)) {
isStandard = true;
break;
}
}
if(isStandard) continue;
String val = filters.get(key);
if (queryString.length() > 0) {
queryString.append(" ");
}
queryString.append(key).append(":").append(val);
}
String escapedQuery = queryString.toString();
try {
escapedQuery = URLEncoder.encode(escapedQuery, "UTF-8");
} catch (UnsupportedEncodingException e) {
// oops.. what to do?
e.printStackTrace();
}
return OpenSearchRequestParser.SEARCH_QUERY + "=" + escapedQuery + "&"
+ OpenSearchRequestParser.SEARCH_RESULTS + "=" + numPerPage + "&"
+ OpenSearchRequestParser.START_PAGE + "=" + pageNum;
}
/**
* Set the request URL.
* Also populates request url cleaned.
* @param urlStr Request URL.
* @throws URIException
*/
public void setRequestUrl(String urlStr) throws URIException {
if (!urlStr.startsWith("http://")) {
urlStr = "http://" + urlStr;
}
// If its not http, next line throws exception. TODO: Fix.
UURI requestURI = UURIFactory.getInstance(urlStr);
put(WaybackConstants.REQUEST_URL_CLEANED, requestURI.toString());
put(WaybackConstants.REQUEST_URL, urlStr);
}
/**
* @return StringFormatter based on user request info
*/
public StringFormatter getFormatter() {
if(formatter == null) {
setLocale(Locale.getDefault());
}
return formatter;
}
public WaybackRequest clone() {
WaybackRequest wbRequest = new WaybackRequest();
wbRequest.contextPrefix = contextPrefix;
wbRequest.resultsPerPage = resultsPerPage;
wbRequest.pageNum = pageNum;
wbRequest.contextPrefix = contextPrefix;
wbRequest.serverPrefix = serverPrefix;
wbRequest.formatter = formatter;
wbRequest.filters = new HashMap<String,String>();
Iterator<String> itr = filters.keySet().iterator();
while(itr.hasNext()) {
String key = itr.next();
String val = filters.get(key);
wbRequest.filters.put(key, val);
}
return wbRequest;
}
/**
* @return the context
*/
public AccessPoint getContext() {
return context;
}
/**
* @param context the context to set
*/
public void setContext(AccessPoint context) {
this.context = context;
}
public ObjectFilter<SearchResult> getExclusionFilter() {
return exclusionFilter;
}
public void setExclusionFilter(ObjectFilter<SearchResult> exclusionFilter) {
this.exclusionFilter = exclusionFilter;
}
}