/*
ESUP-portail is a french academic project developed under the GPL (General Public License) augmented according to the following :
A binary or source file developped by ESUP-portail can be used or compiled with products under Apache license.
The official english text of the GPL can be found here : http://www.gnu.org/licenses/gpl.html .
A non official french translation can be found here : http://www.linux-France.org/article/these/gpl.html .
The different kinds of licenses governing the products developed by the Apache foundation can be found here : http://www.apache.org/licenses .
It follows that you can as well as use download contents as well modify and redistribute them provided you respect the GPL terms.
Downloading and using such contents do not provide any guaranty.
Be sure that you have well understood the terms of the license before using the contents it covers.
The ESUP-portail distribution includes the following distributions :
    * UPortal : 
      software developed by JA-SIG (Java Architecture - Special Interest Group)
      You can find the license page here : http://mis105.udel.edu/ja-sig/uportal/license.html
    * CAS :
      SSO solution developed by Yale University
      You can find the project page here : http://www.yale.edu/tp/auth
    * Cocoon :
      XML framework distributed by the Apache foundation under Apache license;
      Please find the full text here : http://cocoon.apache.org/2.1/license.html
    * Mod_dav:
      A DAV module for Apache web server
      You can find the project page here : http://www.webdav.org/mod_dav
    * IMP :
      webmail from Horde application framework
      You can find the project page here : http://www.horde.org
    * …. To be completed
*/
 
package org.esupportail.portal.channels.CStockage;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.esupportail.portal.channels.CStockage.channelAction.BufferAction;
import org.esupportail.portal.channels.CStockage.channelAction.Constants;
import org.esupportail.portal.channels.CStockage.config.AuthorizationSpaceParameter;
import org.esupportail.portal.channels.CStockage.config.ChannelConfiguration;
import org.esupportail.portal.channels.CStockage.config.Space;
import org.esupportail.portal.channels.CStockage.exception.AnyVisibleSpaceException;
import org.esupportail.portal.channels.CStockage.exception.ApplicationException;
import org.esupportail.portal.channels.CStockage.exception.DownloadException;
import org.esupportail.portal.channels.CStockage.exception.PropertiesException;
import org.esupportail.portal.channels.CStockage.exception.ServerException;
import org.esupportail.portal.channels.CStockage.exception.ChannelException;
import org.esupportail.portal.channels.CStockage.provider.ChannelResource;
import org.esupportail.portal.channels.CStockage.userManagement.PortalGroupsManagement;
import org.jasig.portal.ChannelCacheKey;
import org.jasig.portal.ChannelStaticData;
import org.jasig.portal.ICacheable;
import org.jasig.portal.IMimeResponse;
import org.jasig.portal.PortalControlStructures;
import org.jasig.portal.PortalException;
import org.jasig.portal.channels.BaseChannel;
import org.jasig.portal.security.IPerson;
import org.jasig.portal.utils.XSLT;
import org.xml.sax.ContentHandler;

/**
 * Id: CStockage.java,v 1.0 7 avr. 2004<br/>
 * Copyright (c) 2004 Esup Portail (www.esup-portail.org)<br/>
 * Classes: CStockage<br/>
 * Original Author: Yohan Colmant<br/>
 * Channel used to manage personal files on a webdav server through the portal<br/>
 */




public class CStockage extends BaseChannel implements IMimeResponse,ICacheable  {


	/**
	 * Logger object
	 */
	protected static final Log log = LogFactory.getLog(CStockage.class);
	
	
	
	
  private ChannelStaticData staticData;
  
  private String fileId;
  private PortalControlStructures pcs;


  	/**
  	 * The config object
  	 */
  	private ChannelConfiguration config;
  
  
	/**
	 * The actual mod in which is the channel
	 */
	private String currentMode;

	
	/**
	 * The spaces availables
	 */
	private ArrayList spaces;
	
	/**
	 * The current used space
	 */
	private Space currentSpace;
	
	
	
	
	
	/**
	 * Error at the beginning of the channel
	 */
	private ChannelException setStaticDataException;		
	
	
	
	
	
	/**
	 * User login
	 */
	private String portalLogin;
	
	/**
	 * The webdav login
	 */
	//private String webdavLogin;
	
	/**
	 * The user groups
	 */
	private Vector userGroups;
	

	
	
	
	/**
	 * Object used to keep the objects used between 2 actions, for example, the clipboard
	 */
	private BufferAction buffer;
	
	
	
	

	
	
	
	/**
	 * Constructor
	 */
	public CStockage() {				
		
		//System.out.println("\n\n\n\n\n\n");
		
		// we begin in this mode
		this.currentMode = Constants.SHOW_CURRENT_DIR_MODE;
		
		// init the buffer
		this.buffer = new BufferAction();
	}	

	
	/**
	 * Receive static channel data from the portal. Satisfies implementation of IChannel Interface.
	 * 
	 * @param sd <b>ChannelStaticData</b> static channel data
	 */
	public void setStaticData(ChannelStaticData sd) {		
		
		this.staticData = sd;			
		
		try {
			
			config = new ChannelConfiguration();
			
			IPerson person = sd.getPerson();			
			
			// the usernameAttribute
			String userNameAttribute = config.getUsernameAttribute();
			
			// Set the user login			
			portalLogin = (String)person.getAttribute(userNameAttribute);	
			
			// the user groups
			userGroups = PortalGroupsManagement.getUserGroupsKeys(portalLogin);
			
			// the spaces availables
			spaces = config.getSpaces();
			
			
			
			// for each Space, we check if the path is in the {...} form. Is yes, we replace {...} by the real value
			// after that, we try to obtain the redirected url. for example, if the path is /server/~bidon, we try to have the real path: for example /slide/files/storage/users/n/no/bidon			
			for (int i=0; i<spaces.size(); i++) {
				
				Space space = (Space)spaces.get(i);				

				// if the server access is valid
				boolean validAccess = false;
				
				// check the authorization
				ArrayList authorizations = space.getAuthorization();
				if (authorizations!=null && !authorizations.isEmpty()) {
					
					for (int j=0; !validAccess && j<authorizations.size(); j++) {
						AuthorizationSpaceParameter auth = (AuthorizationSpaceParameter)authorizations.get(j);
						String attribute = auth.getAttribute();
						String value = auth.getValue();
												
						Object[] userValues = (Object[])person.getAttributeValues(attribute);						
						if (userValues!=null) {							
							
							for (int k=0; !validAccess && k<userValues.length; k++) {
								
								String userValue = (String)userValues[k];
								if (userValue.toLowerCase().equals(value.toLowerCase())) {
									validAccess = true;
									log.debug("setStaticData"+" :: "+attribute+" == "+value+" ??? yes");
								}
								else log.debug("setStaticData"+" :: "+attribute+" == "+value+" ??? no, "+attribute+" = "+userValue);
							}							
							
						}
						else log.debug("setStaticData"+" :: "+attribute+" == "+value+" ??? no, "+attribute+" is null ");
					}					
				}
				else {
					validAccess = true;
				}
				
				// if authorized
				if (validAccess) {					
					
					validAccess = false;
				
					// cas authentication mode
					if (space.isCasAuthentication()) {
					
						//TODO validAccess = space.setWebdavAccessObject(davAccessClass, uri, trustedLogin, password);
					}
				
					// trusted or asked authentication mode
					else if (space.isTrustedAuthentication() || space.isAskedAuthentication()){
					
						// if there is a trusted login
						String trustedLogin = space.getLogin();
						String accessLogin = null;
						if (trustedLogin==null || trustedLogin.equals("")) {
							accessLogin = portalLogin;
							space.setLogin(accessLogin);
						}
						else accessLogin = trustedLogin;
					
						// check the {...}
						space.checkPath(person);//TODO, userNameAttribute, accessLogin);					
					
						// we configure the object used by the space to acces the server
						validAccess = space.setServerAccessObject();					
					}
					
				}		
				
				// we check if this space if valid or not
				if (!validAccess) {
					if (log.isDebugEnabled()){
						log.debug("setStaticData"+" :: "+portalLogin+" can not see "+space.getKey());
					}
					
					spaces.remove(i);
					i--;
				}
				
				else {				
					// set for the space, the channel action object used
					boolean wellDone = space.setChannelActionObject(spaces, config, buffer, this.portalLogin, this.userGroups);
					
					if (!wellDone) {
						spaces.remove(i);
						i--;
					}
				}
			}				
			
			// if we can not see any space
			if (spaces.isEmpty()) {
				if (log.isDebugEnabled()){
					log.debug("setStaticData"+" :: AnyVisibleSpaceException");
				}
				
				throw new AnyVisibleSpaceException();
			}
			
			else {
				// the current space
				this.currentSpace = (Space)spaces.get(0);									
			}			
				
		}		
		catch(AnyVisibleSpaceException e) {			
			//this.currentSpace.setWebdavAccessObject(null);
			setStaticDataException = e;
			if (log.isDebugEnabled()){
				log.debug("setStaticData"+" :: "+e);
			}			
		}
		catch(ServerException e) {			
			//this.currentSpace.setWebdavAccessObject(null);
			setStaticDataException = e;
			if (log.isDebugEnabled()){
				log.debug("setStaticData"+" :: "+e);
			}
		}
		catch(ApplicationException e) {			
			//this.currentSpace.setWebdavAccessObject(null);			
			setStaticDataException = e;
			if (log.isDebugEnabled()){
				log.debug("setStaticData"+" :: "+e);
			}
		}
		catch(PropertiesException e) {			
			//this.currentSpace.setWebdavAccessObject(null);		
			setStaticDataException = e;
			if (log.isDebugEnabled()){
				log.debug("setStaticData"+" :: "+e);
			}
		}
		catch(ChannelException e) {			
			//this.currentSpace.setWebdavAccessObject(null);
			setStaticDataException = e;
			if (log.isDebugEnabled()){
				log.debug("setStaticData"+" :: "+e);
			}
		}
		catch(ClassNotFoundException e) {			
			//this.currentSpace.setWebdavAccessObject(null);		
			setStaticDataException = new PropertiesException();
			if (log.isDebugEnabled()){
				log.debug("setStaticData"+" :: "+e);
			}
		}
		catch(IllegalAccessException e) {			
			//this.currentSpace.setWebdavAccessObject(null);		
			setStaticDataException = new PropertiesException();
			if (log.isDebugEnabled()){
				log.debug("setStaticData"+" :: "+e);
			}
		}
		catch(InstantiationException e) {			
			//this.currentSpace.setWebdavAccessObject(null);		
			setStaticDataException = new PropertiesException();
			if (log.isDebugEnabled()){
				log.debug("setStaticData"+" :: "+e);
			}
		}
		catch(MalformedURLException e) {			
			//this.currentSpace.setWebdavAccessObject(null);	
			setStaticDataException = new PropertiesException();
			if (log.isDebugEnabled()){
				log.debug("setStaticData"+" :: "+e);
			}
		}		
	}
	
	
	
	
		
	
	/**
	 * Output channel content to the portal
	 * 
	 * @param out a sax document handler
	 */
	public void renderXML(ContentHandler out) throws PortalException {		
		
		// if we change the space
		String[] spaceChanges = runtimeData.getParameterValues("space");
		if (spaceChanges != null){
			String oldSpaceKey = this.currentSpace.getKey();
			String newSpaceKey = null;
			boolean findChangement = false;
			for (int i = 0; i<spaceChanges.length && !findChangement;i++){
				newSpaceKey = spaceChanges[i];
				if (!newSpaceKey.equals(oldSpaceKey))
					findChangement = true;
			}
			
			// if found, we look for the good space into the ArrayList
			if (findChangement) {
				
				boolean findGoodSpace = false;
				for (int i=0; i<spaces.size() && !findGoodSpace; i++) {
					Space s = (Space)spaces.get(i);
					if (s.getKey().equals(newSpaceKey)) {
						findGoodSpace = true;
						this.currentSpace = s;
					}
						
				}
				
			}
		}
		
		
		// Create a new XSLT styling engine		
		XSLT xslt = new XSLT(this);
		
		// if no space authorized
		if (this.currentSpace == null) {
			StringBuffer xml = new StringBuffer();
			xml.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");		
			xml.append("<ROOT>");
			xml.append("<ERRORS>");
			xml.append("<ERROR channelCode=\""+setStaticDataException.getErrorCode()+"\"/>");
			xml.append("</ERRORS>");
			xml.append("</ROOT>");
			
			xslt.setXSL("CStockage.ssl", "CStockage", runtimeData.getBrowserInfo());
			xslt.setXML(xml.toString());
			xslt.setTarget(out);			
			xslt.transform();
		}
		 else {
		 	// generate the xml for in function of the current space type 
		 	this.currentSpace.getChannelActionObject().checkCalledAction(staticData, runtimeData, out, xslt, setStaticDataException);
		 }
	}
	
	
	


/***************************************************************************/
/*********************START OF DOWNLOAD FUNCTIONS***************************/
/***************************************************************************/

	/**
	 * The resource to download
	 */
	private ChannelResource downloadResource = null;
	
	
  	/**
	 * Return the content type of the seleted file
	 * @see org.jasig.portal.IMimeResponse#getContentType()
	 */
	public String getContentType() {
		return downloadResource.getContentType();
	}


	/**
	 * Send HTTP header
	 * @see org.jasig.portal.IMimeResponse#getHeaders()
	 */
	public Map getHeaders() {
		Map map = new HashMap();
      
		fileId = runtimeData.getParameter("downloadFile");
		//System.out.println("fileId: "+fileId); 
		try {
			fileId = URLDecoder.decode(fileId,"UTF-8");
			//System.out.println("fileId: "+fileId);
		}
		catch (UnsupportedEncodingException e) {
			log.error("getHeaders"+" :: "+e);			
		}
    
		// we check if it is a directory
		String path = currentSpace.getPath();
		
		try {
			//System.out.println("path: "+path);
			downloadResource = this.currentSpace.getServerAccessObject().download(fileId,path);
				
			fileId = fileId.replaceAll(" ", "_");
			fileId = fileId.replaceAll("[àäâã]", "a");
			fileId = fileId.replaceAll("[éêèë]", "e");
			fileId = fileId.replaceAll("[îïì]", "i");
			fileId = fileId.replaceAll("[ôöòõ]", "o");
			fileId = fileId.replaceAll("[ûüù]", "u");
			fileId = fileId.replaceAll("ç", "c");
			fileId = fileId.replaceAll("ñ", "n");
			fileId = fileId.replaceAll("[ÀÄÂÃ]", "A");
			fileId = fileId.replaceAll("[ÉÈËÊ]", "E");
			fileId = fileId.replaceAll("[ÌÏÎ]", "I");
			fileId = fileId.replaceAll("[ÒÖÔÕ]", "O");
			fileId = fileId.replaceAll("[ÙÜÛ]", "U");
			fileId = fileId.replaceAll("Ç", "C");
			fileId = fileId.replaceAll("Ñ", "N");		
			
			// open in new window : inline  -- ||
			// download : attachment  -- -- -- ||
			//                                 \/
			map.put("Content-disposition", "attachment; filename="+fileId  );

			return map;
		}
		catch(DownloadException e) {
			log.error("getHeaders"+" :: "+e);
			map.put("Location", runtimeData.getBaseActionURL());
			return map;
		}
	}
	
	
	/**
	 *	Get the strem for the file to dowload
	 * @see org.jasig.portal.IMimeResponse#getInputStream()
	 */
	public InputStream getInputStream() throws IOException {	
		return downloadResource.getInputStream();
	}


	/**
	 * Function unused here
	 * @see org.jasig.portal.IMimeResponse#downloadData(java.io.OutputStream)
	 */
	public void downloadData(OutputStream arg0) throws IOException {
	}


	/**
	 * Function unused here
	 * @see org.jasig.portal.IMimeResponse#getName()
	 */
	public String getName() {
		return null;
	}


	/**
	 * Print the error generated by the download
	 * @see org.jasig.portal.IMimeResponse#reportDownloadError(java.lang.Exception)
	 */
	public void reportDownloadError(Exception arg0) {
		log.error("reportDownloadError"+" :: "+arg0);
	}

	

	/***************************************************************************/
	/*********************END OF DOWNLOAD FUNCTIONS*****************************/
	/***************************************************************************/



	/***************************************************************************/
	/**********************START OF CACHE FUNCTIONS*****************************/
	/***************************************************************************/

	/* (non-Javadoc)
	 * @see org.jasig.portal.ICacheable#generateKey()
	 */
	public ChannelCacheKey generateKey() {

	    ChannelCacheKey k = new ChannelCacheKey();
	    StringBuffer sbKey = new StringBuffer(1024);
	    sbKey.append(this.getClass().getName() + " : ");
	    sbKey.append("login : "+portalLogin+";");
	    sbKey.append("SubscribeId:"+this.staticData.getChannelSubscribeId());
	    k.setKeyScope(ChannelCacheKey.INSTANCE_KEY_SCOPE);

	    k.setKey(sbKey.toString());
	    k.setKeyValidity(new Long(System.currentTimeMillis()));
	    /*LogService.log(LogService.DEBUG,
	                              "CStockage::generateKey() (id : " +
	                              this.staticData.getChannelSubscribeId() +
	                              ") : generateKey :  -> Cle de cache:" + k.getKey());
	    */
		return k;
	}

	/* (non-Javadoc)
	 * @see org.jasig.portal.ICacheable#isCacheValid(java.lang.Object)
	 */
	public boolean isCacheValid(Object validity) {

	    boolean cacheValid = false;

	  if(runtimeData.getParameters().size()==0)
	  		cacheValid=true;

	  /*if (cacheValid) {
        LogService.log(LogService.DEBUG,
                                  "CStockage::isCacheValid (" +
                                  this.staticData.getChannelSubscribeId() +
                                  ") : isCacheValid -> YES");
      }
      else {
        LogService.log(LogService.DEBUG,
                                  "CStockage::isCacheValid (" +
                                  this.staticData.getChannelSubscribeId() +
                                  ") : isCacheValid -> NO");
      }*/
	    return cacheValid;
	}
	/***************************************************************************/
	/************************END OF CACHE FUNCTIONS*****************************/
	/***************************************************************************/
}
