package com.idega.presentation.ui;
import java.io.IOException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.idega.core.builder.business.BuilderService;
import com.idega.core.builder.business.ICBuilderConstants;
import com.idega.core.builder.data.ICPage;
import com.idega.presentation.IWContext;
import com.idega.presentation.Page;
import com.idega.presentation.PresentationObject;
import com.idega.util.FileUtil;
import com.idega.util.Index;
import com.idega.util.IndexComparator;
import com.idega.util.URLUtil;
import com.idega.util.text.TextSoap;
/**
* A presentationObject that uses FileUtil.getStringFromURL to serverside include a given URL
*@author <a href="mailto:eiki@idega.is">Eirikur Hrafnsson</a>
*@version 1.0
*/
public class PageIncluder extends PresentationObject implements Index{
private static final String IB_PAGE_PARAMETER = ICBuilderConstants.IB_PAGE_PARAMETER;
private String URL = null;
private String BASEURL = null;
private String BASEURLHTTPS = null;
private String RELATIVEURL = null;
private String pageIncluderPrefix = null;
private String _label = null;
private String _sendToLabel = null;
private ICPage _sendToPage = null;
private String _sendToPageIfSet = null;
private String sessionId = null;
private String sessionURL = null;
private String token = null;
private String tokenReplacer = null;
private String serverName = null;
private String httpPrefix = null;
private String httpsPrefix = null;
private Map findReplaceStrings = null;
private String out;
private int index = 1000;
private static final String PAGE_INCLUDER_PARAMETER_NAME="iw_uri_";
private static final String PAGE_INCLUDER_SESSION_NAME="iw_session_token";
private String pageIncluderFinalParamName = null;
private int instanceId;
private boolean forceFrame = false;
private boolean useSecureLinks = false;
private boolean changeURL = false;
private final String symbol = "$";
private Map allowedDomainsAndIPNumberMap;
public PageIncluder(){
super();
}
public PageIncluder(String URL){
this();
this.URL = URL;
}
private void sortAndProcess(IWContext iwc){
//sort
Page parent = this.getParentPage();/**@todo get in main**/
List objects = parent.getChildrenRecursive();
ArrayList includers = new ArrayList();
Iterator iter = objects.iterator();
while (iter.hasNext()) {
Object item = iter.next();
if(item instanceof PageIncluder){
includers.add(item);
}
}
IndexComparator indexer = new IndexComparator(IndexComparator.ORDER_BY_INDEX);
includers = indexer.sortedArrayList(includers);
//process
Iterator iter2 = includers.iterator();
while (iter2.hasNext()) {
PageIncluder item = (PageIncluder)iter2.next();
try {
item.process(iwc);
}
catch (Exception ex) {
ex.printStackTrace(System.err);
}
}
}
public void setIndex(int index){
this.index = index;
}
public int getIndex(){
return this.index;
}
public void main(IWContext iwc) throws Exception {
Page fromPage = this.getParentPage();
this.instanceId=getICObjectInstanceID();
this.changeURL = (iwc.isParameterSet(PAGE_INCLUDER_PARAMETER_NAME+this._label)) || (iwc.isParameterSet(PAGE_INCLUDER_PARAMETER_NAME+this.instanceId));
if(this.changeURL){
this.changeURL = canChangeURLFromRequest(iwc);
}
if( this.changeURL && (this._sendToPage != null) && iwc.isParameterSet(this._sendToPageIfSet) ) {//forwarding
forwardToIBPage(fromPage,this._sendToPage,iwc);
}
else if(this.out==null) {
sortAndProcess(iwc);//ususal
}
}
/**
* Security check to stop hackers from using the pageincluder from a domain that is not allowed
* @param iwc
*/
private boolean canChangeURLFromRequest(IWContext iwc) {
if(this.allowedDomainsAndIPNumberMap!=null){
boolean changingTheURLFromRequest = (iwc.isParameterSet(PAGE_INCLUDER_PARAMETER_NAME+this._label)) || (iwc.isParameterSet(PAGE_INCLUDER_PARAMETER_NAME+this.instanceId));
if(changingTheURLFromRequest){
String url = iwc.getParameter(PAGE_INCLUDER_PARAMETER_NAME+this._label);
if(url==null || "".equals(url)){
url = iwc.getParameter(PAGE_INCLUDER_PARAMETER_NAME+this.instanceId);
}
URLUtil util = new URLUtil(url);
String domainOrIpPart = util.getHost();
if(!this.allowedDomainsAndIPNumberMap.containsKey(domainOrIpPart)){
return false;//if the domain is not allowed to ask for pageincluding then return false
}
}
}
return true;
}
public void print(IWContext iwc)throws IOException{
if(this.URL!=null){
if(this.out!=null) {
println(this.out);
}
this.out = null;
}
}
protected void process(IWContext iwc)throws Exception{
this.serverName = iwc.getServerName();
this.instanceId=getICObjectInstanceID();
//pageincluderprefix httpprefix etc...
setPrefixes(iwc);
//get parameters and change the pageincluders url if needed
String loc = getLocation(iwc);
//System.out.println("Loc before = "+loc);
//get a session id from a session creating page
if( (this.sessionURL!=null) && (this.token!=null) ){
if( this.sessionId==null ){
this.sessionId = (String) iwc.getSessionAttribute( PAGE_INCLUDER_SESSION_NAME );
if(this.sessionId==null){
this.sessionId = FileUtil.getStringFromURL(this.sessionURL);
//debug("Sessions id is : "+sessionId);
}
}
iwc.setSessionAttribute(PAGE_INCLUDER_SESSION_NAME, this.sessionId);
loc = TextSoap.findAndReplace(loc,this.token,this.sessionId);
loc = TextSoap.findAndCut(loc,"\r\n");
loc = TextSoap.findAndCut(loc,"\n");
}
else if( (this.sessionId!=null) && (this.token!=null)){
loc = TextSoap.findAndReplace(loc,this.token,this.sessionId);
}
//System.out.println("Location url is: "+loc+" and index is: "+index);
if(loc!=null && !loc.equals("") ){
this.out = FileUtil.getStringFromURL(loc);
URL url = new URL(loc);
this.BASEURL = url.getProtocol()+"://"+url.getHost()+"/";
this.BASEURLHTTPS = "https://"+url.getHost()+"/";
if(loc.lastIndexOf("/")==6) {
loc+="/";
}
this.RELATIVEURL = loc.substring(0,loc.lastIndexOf("/")+1);
/**
* @todo use expressions to make none case sensitive or implement using HTMLDocumentLoader (Advanced Swing);
* **/
/* Finish if needed but make sure it is a configurable option
* just an idea to get all javascripts and styles from the real page
List headerTag = TextSoap.FindAllBetween(out, "<head>","</head>");
if(headerTag!=null && headerTag.isEmpty()) {
String headerContent = (String)headerTag.iterator().next();
List styles = TextSoap.FindAllBetween(out, "<style type=\"text/css\">\n","</style>\n");
if(styles!=null && styles.isEmpty()) {
this.getParentPage().setStyleDefinition()...
}
}
*/
this.out = TextSoap.stripHTMLTagAndChangeBodyTagToTable(this.out);
this.out = preProcess(this.out,iwc);
if( this.forceFrame ){
this.out = encodeQueryStrings(this.out);
}
this.out = changeAHrefAttributes(this.out);
this.out = changeFormActionAttributes(this.out);
this.out = changeSrcAttributes(this.out);
this.out = changeJSOpenWindowURL(this.out);
this.out = postProcess(this.out,iwc);
}
}
protected String preProcess(String html,IWContext iwc){
html = TextSoap.findAndReplace(html,"href=\"javascript","httpIW_PREPROCESSED");
return html;
}
protected String postProcess(String html,IWContext iwc){
html = TextSoap.findAndReplace(html,"httpIW_PREPROCESSED","href=\"javascript");
html = findAndReplaceStrings(html);
//Make images from this server (idegaweb) always follow the protocol being used http/https
//@todo this is case sensitive and could break! move to IWContext. Also done in Link, SubmitButton, Image and PageIncluder
if( iwc.getRequest().isSecure() ){
html = TextSoap.findAndReplace(html,"src=\"http://"+this.serverName,"src=\"https://"+this.serverName);
}
return html;
}
/**@todo temporary Strengs fix. We should change the find replace method with the simpler:<br>
* 1. find all between symbol=".." strings
* 2. Change them
* 3. Find replace the original found strings with the new ones
*/
private String findAndReplaceStrings(String html){
if( this.findReplaceStrings != null ){
Set keys = this.findReplaceStrings.keySet();
Iterator iter = keys.iterator();
String key;
while (iter.hasNext()) {
key = (String)iter.next();
html = TextSoap.findAndReplace(html,key,(String)this.findReplaceStrings.get(key));
}
}
return html;
}
public void setFindAndReplaceString(String stringToFind, String stringToReplace){
if( this.findReplaceStrings == null ) {
this.findReplaceStrings = new HashMap();
}
this.findReplaceStrings.put(stringToFind,stringToReplace);
}
protected String changeAHrefAttributes(String html){
/*
Possibilities tags: ahref and action src
/xxx/xx prefix+baseurl+/xxx/xx baseurl+/xxx/xx
xxx/xx prefix+relativeurl+/+xxx/xx relative+/+xxx/xx
http:// prefix ekkert
//slashdot.org/ prefix+http: http:
*/
html = insertPageIncludeInTagIgnoreCase("href",html);
return html;
}
protected String changeFormActionAttributes(String html){
html = insertPageIncludeInTagIgnoreCase("action",html);
return html;
}
protected String changeSrcAttributes(String html){
html = changeURLToAbsoluteValueIgnoreCase("src",html);
html = changeURLToAbsoluteValueIgnoreCase("background",html);
return html;
}
protected String insertPageIncludeInTagIgnoreCase( String tag,String html){
html = insertPageIncludeInTag(tag.toLowerCase(),html);
html = insertPageIncludeInTag(tag.toUpperCase(),html);
return html;
}
protected String insertPageIncludeInTag(String tag,String html){
//do NOT change the order of replacements!
// "logic" is as follows
/* prefix of url to replace -> what changes to
1.
nothing -> http+relativeurl = baseurl
// -> baseurl
/ -> baseurl
http -> donothing = baseurl
https -> donothing = httpsbaseurl
http to another server -> do nothing
2. force in frame addon
baseurl -> httpprefix+baseurl
2b.
httpprefix+baseurl -> nothing
httpsbaseurl -> httpsprefix+httpsbaseurl
*/
tag = tag+"=\"";
String prefixHttp= tag+this.httpPrefix;
String prefixHttps= tag+this.httpsPrefix;
html = TextSoap.findAndReplace(html,tag+"//", tag+this.BASEURL);// the // case
html = TextSoap.findAndReplace(html,tag+"/", tag+this.BASEURL);
String[] unchangedUrlsPrefixes = new String[] {"http:", "ftp:", "mailto:", "https:"}; // prefixes of urls not to modify, add as needed
html = TextSoap.findAndReplace(html,tag, unchangedUrlsPrefixes,tag+this.RELATIVEURL);
//System.out.println("tag+BASEURL"+tag+BASEURL);
//System.out.println("tag+RELATIVEURL"+tag+RELATIVEURL);
if(this.forceFrame){
html = TextSoap.findAndReplace(html,tag+this.BASEURL,prefixHttp+this.BASEURL);// the http://baseurl case skipped the tailing /
html = TextSoap.findAndReplace(html,tag+this.BASEURLHTTPS,prefixHttps+this.BASEURL);// the https:// case
}
return html;
}
protected String getCurrentIBPageIDToURLString(IWContext iwc)throws Exception{
BuilderService bservice = getBuilderService(iwc);
return IB_PAGE_PARAMETER+"="+bservice.getCurrentPageId(iwc);
}
protected String getSendToPageURLString(){
return IB_PAGE_PARAMETER+"="+this._sendToPage.getID();
}
protected String changeURLToAbsoluteValueIgnoreCase(String tag,String html){
html = changeURLToAbsoluteValue(tag.toLowerCase(),html);
html = changeURLToAbsoluteValue(tag.toUpperCase(),html);
return html;
}
protected String changeURLToAbsoluteValue(String tag,String html){
html = TextSoap.findAndReplace(html,tag+"=\"//",tag+"=\""+"http://");// the // case
html = TextSoap.findAndReplace(html,tag+"=\"/",tag+"=\""+this.BASEURL );// the / case
html = TextSoap.findAndReplace(html,tag+"=\"","http://",tag+"=\""+this.RELATIVEURL);
return html;
}
private String symbolReplace(String html, String tag){
return TextSoap.findAndReplace(html,this.symbol+tag,"&"+tag);
}
protected String encodeQueryStrings(String html){
//laddi changed links in idegaweb to use amp; instead of a &
//so we need to fix that here!
html = TextSoap.findAndReplace(html,"&","&");
//laddi again, only replacing single &, a javascript issue
html = TextSoap.findAndReplace(html,"&","&",this.symbol);
//fixing this should be done with a HTMLEditor object OR
//make a single general expression fix
//by getting all between (textSoap) changing them and then use a find replace on the originals
html = symbolReplace(html,"eth;");
html = symbolReplace(html,"ETH;");
html = symbolReplace(html,"thorn;");
html = symbolReplace(html,"THORN;");
html = symbolReplace(html,"aelig;");
html = symbolReplace(html,"AElig;");
html = symbolReplace(html,"ouml;");
html = symbolReplace(html,"Ouml;");
html = symbolReplace(html,"auml;");
html = symbolReplace(html,"Auml;");
html = symbolReplace(html,"euml;");
html = symbolReplace(html,"Euml;");
html = symbolReplace(html,"uuml;");
html = symbolReplace(html,"Uuml;");
html = symbolReplace(html,"nbsp;");
// html = symbolReplace(html,"amp;");// a muuu point see top of method
html = symbolReplace(html,"quot;");
html = symbolReplace(html,"middot");
html = symbolReplace(html,"raquo;");
html = symbolReplace(html,"#149;");
html = symbolReplace(html,"#039;");
html = symbolReplace(html,"#169;");
html = symbolReplace(html,"#8211;");
html = symbolReplace(html,"gt;");
html = symbolReplace(html,"pound;");
html = symbolReplace(html,"yen;");
html = symbolReplace(html,"copy;");
html = symbolReplace(html,"reg;");
html = symbolReplace(html,"szlig;");
html = symbolReplace(html,"#cedil;");
html = symbolReplace(html,"ccedil;");
html = symbolReplace(html,"Ccedil;");
html = symbolReplace(html,"cedil;");
html = symbolReplace(html,"oslash;");
html = symbolReplace(html,"Oslash;");
html = TextSoap.findAndReplace(html," "+this.symbol+" "," & ");
html = TextSoap.findAndReplace(html,this.symbol+" ","& ");
//islenskir broddstafir
html = symbolReplace(html,"aacute;");
html = symbolReplace(html,"Aacute;");
html = symbolReplace(html,"eacute;");
html = symbolReplace(html,"Eacute;");
html = symbolReplace(html,"iacute;");
html = symbolReplace(html,"Iacute;");
html = symbolReplace(html,"uacute;");
html = symbolReplace(html,"Uacute;");
html = symbolReplace(html,"oacute;");
html = symbolReplace(html,"Oacute;");
html = symbolReplace(html,"yacute;");
html = symbolReplace(html,"Yacute;");
return html;
}
protected String decodeQueryString(String query){
return TextSoap.findAndReplace(query,this.symbol,"&");
}
protected String copyJavaScript(String html,IWContext iwc){
return html;
}
protected String copyIncludes(String html,IWContext iwc){
return html;
}
public synchronized Object clone() {
PageIncluder obj = null;
try {
obj = (PageIncluder)super.clone();
obj.URL = this.URL;
obj.BASEURL = this.BASEURL;
obj.RELATIVEURL = this.RELATIVEURL;
obj.pageIncluderPrefix = this.pageIncluderPrefix;
obj.instanceId = this.instanceId;
obj.sessionId = this.sessionId;
obj.sessionURL = this.sessionURL;
obj.token = this.token;
obj.tokenReplacer = this.tokenReplacer;
obj.out = this.out;
obj.index = this.index;
}
catch(Exception ex) {
ex.printStackTrace(System.err);
}
return obj;
}
public void setURL(String URL){
this.URL = URL;
}
public void setSessionId(String sessionId){
this.sessionId = sessionId;
}
public void setURLToGetSessionIDFrom(String sessionURL){
this.sessionURL = sessionURL;
}
public void setTokenToReplaceWithSessionId(String token){
this.token = token;
}
public void setForceInFrame(boolean forceFrame){
this.forceFrame = forceFrame;
}
public void setKeepSecure(boolean useSecureLinks){
this.useSecureLinks = useSecureLinks;
}
public void setLabel(String label) {
this._label = label;
}
/**
* Redirects the URL from this PageIncluder to another PageIncluder with the
* corresponding label.
*
* @param label The label of the PageIncluder which we want to redirect to.
*/
public void setRedirectTo(String label) {
this._sendToLabel = label;
}
public String getLabel() {
return this._label;
}
public String getRedirectTo() {
return this._sendToLabel;
}
public void setSendToPage(ICPage page) {
this._sendToPage = page;
}
public ICPage getSendToPage() {
return this._sendToPage;
}
public void setSendToPageIfSet(String condition) {
this._sendToPageIfSet = condition;
}
public String getSendToPageIfSet() {
return this._sendToPageIfSet;
}
public void forwardToIBPage(Page fromPage ,ICPage page, IWContext iwc) throws Exception{
StringBuffer URL = new StringBuffer();
BuilderService bservice = getBuilderService(iwc);
URL.append(bservice.getPageURI(((Integer)page.getPrimaryKeyValue()).intValue()));
URL.append('&');
String query = getRequest().getQueryString();
if( this._sendToLabel != null ){
query = TextSoap.findAndReplace(query,PAGE_INCLUDER_PARAMETER_NAME+this.instanceId,PAGE_INCLUDER_PARAMETER_NAME+this._sendToLabel);
}
URL.append(query);
fromPage.setToRedirect(URL.toString());
fromPage.empty();
}
/**
* changes the pageincluders url if needed and adds all parameters from the request.
*/
private String getLocation(IWContext iwc){
StringBuffer location = new StringBuffer();
String query = null;
StringBuffer queryBuf = new StringBuffer();
String instanceParam = PAGE_INCLUDER_PARAMETER_NAME+this.instanceId;
String labelParam = PAGE_INCLUDER_PARAMETER_NAME+this._label;
//get all parameters even from post actions
Enumeration enumer = iwc.getParameterNames();
while (enumer.hasMoreElements()) {
String param = (String) enumer.nextElement();
//debug(param+" : "+iwc.getParameter(param));
if ( param.equals(instanceParam) || param.equals(labelParam) ){
boolean canChangeURL = canChangeURLFromRequest(iwc);
if(canChangeURL){
this.URL = decodeQueryString(iwc.getParameter(param));
}
//System.out.println("Changing location to:"+location.toString());
}
else{
if (param.indexOf(PAGE_INCLUDER_PARAMETER_NAME) == -1) {
String[] values = iwc.getParameterValues(param);
for (int i = 0; i < values.length; i++) {
queryBuf.append(param);
queryBuf.append("=");
queryBuf.append(URLEncoder.encode(values[i]));
queryBuf.append("&");
}
}
}
}//while ends
query = queryBuf.toString();
location.append(this.URL);
if( !query.equals("") ){
if(this.URL.endsWith("/")){//check if the url ends with a slash
location.append("?");
}
else{//no slash at end
if( this.URL.indexOf("?")==-1 ){//check if the url contains a ?
if(this.URL.indexOf("/",8)!=-1){//check if the url contains a slash
location.append("?");
}
else{
location.append("/?");
}
}
else{//just add to the parameters
location.append("&");
}
}
//add the extra parameters
location.append(query);
}
String finalLocationString = finalizeLocationString(location.toString(),iwc);
return finalLocationString;
}
/**
* A method for extending classes to influence the location string (the url) that is sent to the real page.
* For example to add extra parameters.
* @param location The finished url to the real page the pageincluder is including
* @return
*/
protected String finalizeLocationString(String location, IWContext iwc) {
return location;
}
private void setPrefixes(IWContext iwc)throws Exception{
if (this.forceFrame ) {
StringBuffer buf = new StringBuffer();
String uri = iwc.getRequestURI();
buf.append(uri);
buf.append('?');
if( this._sendToPage!=null ){
if ( (this._sendToLabel != null) && (this._sendToPageIfSet==null) ){
buf.append(getSendToPageURLString());
buf.append('&');
buf.append(PAGE_INCLUDER_PARAMETER_NAME);
buf.append(this._sendToLabel);
buf.append('=');
}
else{
buf.append(getCurrentIBPageIDToURLString(iwc));
buf.append('&');
buf.append(PAGE_INCLUDER_PARAMETER_NAME);
buf.append(this.instanceId);
buf.append('=');
}
}
else{
buf.append(getCurrentIBPageIDToURLString(iwc));
buf.append('&');
if ( (this._sendToLabel != null) && (this._sendToPageIfSet==null) ){
buf.append(PAGE_INCLUDER_PARAMETER_NAME);
buf.append(this._sendToLabel);
buf.append('=');
}
else{
buf.append(PAGE_INCLUDER_PARAMETER_NAME);
buf.append(this.instanceId);
buf.append('=');
}
}
this.pageIncluderPrefix = buf.toString();
if( this.useSecureLinks ){
buf.append("https://");
buf.append(iwc.getServerName());
}
StringBuffer buf2 = new StringBuffer();
buf2.append("http://").append(this.serverName).append(this.pageIncluderPrefix);
//.append("http://");
this.httpPrefix = buf2.toString();
//System.out.println("httpPrefix"+httpPrefix);
this.httpsPrefix = "https://"+this.httpPrefix.substring(7,this.httpPrefix.length());
//System.out.println("httpSPrefix"+httpsPrefix);
//System.out.println("PAGEINCLUDER PREFIX = "+pageIncluderPrefix);
}
else {
this.pageIncluderPrefix ="";
}
}
private String changeJSOpenWindowURL(String html) {
String regex = ":openwindow\\(\\\'\\/servlet\\/WindowOpener\\??[\\w+\\=\\-?.+\\"+this.symbol+"?]*\\\',";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(html);
StringBuffer sb = new StringBuffer();
while(matcher.find()) {
StringBuffer sbURL = new StringBuffer();
StringBuffer sbSymbol = new StringBuffer();
String rURL = "\\(\\\'\\/servlet";
Pattern pURL = Pattern.compile(rURL);
Matcher mURL = pURL.matcher(matcher.group());
while(mURL.find()) {
mURL.appendReplacement(sbURL,"('"+this.BASEURL+"servlet");
}
mURL.appendTail(sbURL);
String rSymbol = "\\"+this.symbol;
Pattern pSymbol = Pattern.compile(rSymbol);
Matcher mSymbol = pSymbol.matcher(sbURL.toString());
while(mSymbol.find()) {
mSymbol.appendReplacement(sbSymbol,"&");
}
mSymbol.appendTail(sbSymbol);
matcher.appendReplacement(sb,sbSymbol.toString());
}
matcher.appendTail(sb);
return sb.toString();
}
public void setToAddRequiredIPOrDomain(String allowedIpOrDomain){
if(this.allowedDomainsAndIPNumberMap==null){
this.allowedDomainsAndIPNumberMap = new HashMap();
}
this.allowedDomainsAndIPNumberMap.put(allowedIpOrDomain,allowedIpOrDomain);//map to avoid adding endlessly into a list in the builder
}
}