/* ======================================================================
The Bodington System Software License, Version 1.0
 
Copyright (c) 2001 The University of Leeds.  All rights reserved.
 
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
 
1.  Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
 
2.  Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
 
3.  The end-user documentation included with the redistribution, if any,
must include the following acknowledgement:  "This product includes
software developed by the University of Leeds
(http://www.bodington.org/)."  Alternately, this acknowledgement may
appear in the software itself, if and wherever such third-party
acknowledgements normally appear.
 
4.  The names "Bodington", "Nathan Bodington", "Bodington System",
"Bodington Open Source Project", and "The University of Leeds" must not be
used to endorse or promote products derived from this software without
prior written permission. For written permission, please contact
d.gardner@leeds.ac.uk.
 
5.  The name "Bodington" may not appear in the name of products derived
from this software without prior written permission of the University of
Leeds.
 
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO,  TITLE,  THE IMPLIED WARRANTIES
OF QUALITY  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
EVENT SHALL THE UNIVERSITY OF LEEDS OR ITS CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
=========================================================
 
This software was originally created by the University of Leeds and may contain voluntary
contributions from others.  For more information on the Bodington Open Source Project, please
see http://bodington.org/
 
====================================================================== */

package org.bodington.server;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import javax.swing.tree.DefaultMutableTreeNode;

import org.apache.log4j.Logger;
import org.bodington.database.PrimaryKey;
import org.bodington.server.realm.Permission;
import org.bodington.server.resources.Resource;
import org.bodington.server.resources.ResourceUtils;
import org.bodington.server.resources.UploadedFile;
import org.bodington.server.resources.UploadedFileManager;
import org.bodington.server.resources.UploadedFileNode;
import org.bodington.server.resources.UploadedFileSummary;
import org.bodington.text.Metadatum;
import org.bodington.util.FileMover;
import org.bodington.util.Scanner;
import org.bodington.util.ScannerResults;
import org.bodington.util.VirusScanner;
import org.bodington.util.html.HtmlScanner;

public class UploadedFileSessionImpl implements
    UploadedFileSession
{
    
    private static Logger log = Logger.getLogger(UploadedFileSessionImpl.class);
    
    /**
     * The file that contains the list of uploaded files.
     */
    private static final String FILELISTING_XML = "filelisting.xml";

    private BuildingSession buildingSession;
    
    public UploadedFileSessionImpl(BuildingSession buildingSession)
    {
        this.buildingSession = buildingSession;
    }
   
    public UploadedFileSummary copyFile( String current_pathname, String resource_pathname, String mime_type )
    throws BuildingServerException
    {
     return transferFile(current_pathname, resource_pathname, mime_type, false);
    }

    public UploadedFileSummary transferFile( String current_pathname, String resource_pathname, String mime_type )
    throws BuildingServerException
    {
         return transferFile(current_pathname, resource_pathname, mime_type, true);
    }

    private UploadedFileSummary transferFile( String current_pathname, String resource_pathname, String mime_type, boolean deleteOrig)
    throws BuildingServerException
    {
    File source = new File( current_pathname );
    if ( !source.exists() )
        throw new BuildingServerException( "The file to be transfered into the system doesn't exist. [" + source + "]");
    
    if ( source.length() < 1)
        throw new BuildingServerUserException( "The uploaded file was empty");

    Resource r = getResourceWithCheck();
    if ( !r.checkPermission( Permission.UPLOAD ) )
        throw new BuildingServerException( "You don't have access rights to allow you to upload files here." );
    


    // there is one more UploadedFile than tokens because the root folder has no name
    UploadedFile[] path;
    
    QuotaMetadatum quota = getQuotaUpdate();
    
    synchronized (quota.getLock())
    {
        long size = source.length();
        checkQuota(quota, size);
        
        r.checkUpload(resource_pathname, mime_type);
        // Check if we should be scanning uploads for viruses
        if (BuildingContext.getProperty("upload.scan.virus", "false").equals(
            "true"))
        {
            Scanner scanner = VirusScanner.getInstance();
            if (!scanner.scan(source).isOk())
            {
                log.info("Virus found in: " + source.getAbsolutePath());
                throw new BuildingServerUserException(
                    "Upload fail as a virus was detected");
            }
        }
        if (("text/html").equals(mime_type))
        {
            Scanner scanner = new HtmlScanner();
            ScannerResults results = scanner.scan(source);
            if (!results.isOk())
            {
                BuildingServerUserException exception = new BuildingServerUserException(
                    "Problem Transfering File");
                exception.addMessages(results.getMessages());
                throw exception;
            }
        }
        // If it ends with .css (case-insensitive)
        if ((CSSScanner.shouldScan(resource_pathname)))
        {
            Scanner scanner = new CSSScanner();
            ScannerResults results = scanner.scan(source);
            if (!results.isOk())
            {
                BuildingServerUserException exception = new BuildingServerUserException(
                    "Problem Transfering File");
                exception.addMessages(results.getMessages());
                throw exception;
            }
        }
        java.sql.Timestamp now = new java.sql.Timestamp(System
            .currentTimeMillis());
        File target;
        String[] tokens = UploadedFile.parsePath(resource_pathname);
        if (tokens.length < 1)
            throw new BuildingServerUserException(
                "No destination file name was specified.");
        path = new UploadedFile[tokens.length + 1];
        String[] dirPath;
        // build the necessary folders if they don't already exist
        for (int i = 0; i < tokens.length; i++)
        {
            dirPath = new String[i];
            System.arraycopy(tokens, 0, dirPath, 0, i);

            StringBuffer folderPath = new StringBuffer(resource_pathname
                .length());
            for (int pos = 0; pos < dirPath.length; pos++)
            {
                folderPath.append("/");
                folderPath.append(dirPath[pos]);
            }
            path[i] = UploadedFile.findUploadedFile(createFolder(
                folderPath.toString()).getPrimaryKey());

        }
        // now do the file at the end of the path
        String filePath[] = new String[tokens.length];
        System.arraycopy(tokens, 0, filePath, 0, tokens.length);
        log.debug("Checking file: " + filePath[filePath.length - 1]);
        // Create a lock on the directory in which this file will be created.
        path[path.length - 1] = UploadedFile.findUploadedFileByPath(r
            .getResourceId(), filePath);
        if (path[path.length - 1] != null && path[path.length - 1].isFolder())
            throw new BuildingServerUserException(
                "Couldn't upload file - there is a folder with the same name.");
        // create the object so we can ask it for the file
        if (path[path.length - 1] == null)
        {
            log.debug("Making record");
            path[path.length - 1] = new UploadedFile();
            path[path.length - 1].setCreatedTime(now);
            path[path.length - 1].setCreateUserId(getUserId());
            path[path.length - 1].setFlags(0);
            path[path.length - 1].setFolder(false);
            // indicate that file is in process of being uploaded
            // but nowhere do we check this!!
            path[path.length - 1].setSize(1L);
            path[path.length - 1].setBytesUploaded(0L);
            path[path.length - 1].setName(tokens[tokens.length - 1]);

        }
        path[path.length - 1].setMimeType(mime_type);
        path[path.length - 1].setUpdatedTime(now);
        path[path.length - 1].setUpdateUserId(getUserId());
        path[path.length - 1].setDeleted(false);
        if (path[path.length - 1].isUnsaved())
        {
            try
            {
                UploadedFileManager manager = UploadedFileManager
                    .getInstance(r);
                manager.addNode(path[path.length - 2], path[path.length - 1]);
            }
            finally
            {
                UploadedFileManager.release(r);
            }
        }
        else
        {
            path[path.length - 1].save();
        }
        log.debug("Saved record in database.");
        //last element in path - is the file
        target = path[path.length - 1].getFile();
        // delete current uploaded file
        if (target.exists())
        {
            if (!target.isFile())
                throw new BuildingServerUserException(
                    "Couldn't publish file because a file system object of the same name exists.");
            target.delete();
            if (target.exists())
                throw new BuildingServerException(
                    "Couldn't delete old version of file in publishing area.");
        }
        // move file into position
        try
        {
            if (deleteOrig)
                FileMover.moveFile(source, target);
            else
                FileMover.copyFile(source, target);
        }
        catch (IOException ioex)
        {
            log.error(ioex.getMessage(), ioex);
            throw new BuildingServerException("Couldn't move "
                + source.getAbsolutePath() + " to " + target.getAbsolutePath()
                + " in publishing area. " + ioex.getMessage());
        }
        // update size now the file is saved
        path[path.length - 1].setSize(target.length());
        path[path.length - 1].setBytesUploaded(target.length());
        path[path.length - 1].save();
        
        
        quota.adjustUsage(size);
    }    
    log.debug( "Created the file in the host file system" );
    
    invalidateCache();
    
    return path[path.length-1].getSummary();
    
    }

    private void checkQuota(Quota quota, long size) throws BuildingServerException, QuotaExceededException
    {
        if ( hasQuotas() && ( quota.getUsage() + size > quota.getQuota() ) )
            throw new QuotaExceededException(quota, size);
    }

    private boolean hasQuotas() throws BuildingServerException
    {
        if (getResourceWithCheck().checkPermission(Permission.SYSADMIN))
            return false;
        return true;
    }
   
    public UploadedFileSummary createFolder( String resource_pathname )
        throws BuildingServerException
        {
        Resource r = getResourceWithCheck();
        if ( !r.checkPermission( Permission.UPLOAD ) )
            throw new BuildingServerException( "You don't have access rights to allow you to upload files or create folders here." );
            
        r.checkFolder( resource_pathname );
        
        
        java.sql.Timestamp now = new java.sql.Timestamp( System.currentTimeMillis() );
        File target;
        
        // should be capable of creating the root folder.
        
        String[] tokens = UploadedFile.parsePath(resource_pathname);
        String[] parent_tokens = (tokens.length==0)?null:new String[tokens.length-1];
        UploadedFile parent, folder;
            
        if ( tokens.length>0 )
        System.arraycopy( tokens, 0, parent_tokens, 0, tokens.length-1 );
        
        if ( tokens.length==0  )
            parent = null;
        else
        {
            parent = UploadedFile.findUploadedFileByPath( r.getResourceId(), parent_tokens );
            if ( parent == null)
            {
                if (tokens.length == 1)
                {
                    parent = UploadedFile.findUploadedFile(createFolder(null).getPrimaryKey());
                    if (parent == null)
                        throw new BuildingServerException( "Can't create root folder");
                }
                else
                    throw new BuildingServerException( "Can't find the folder to put the new folder in." );
            }
        }
        
        if ( parent!=null && !parent.isFolder() )
            throw new BuildingServerException( "Can't create folders inside files." );

        if ( parent!=null && parent.isDeleted() )
            throw new BuildingServerException( "Can't create a folder inside a deleted folder." );

        folder = UploadedFile.findUploadedFileByPath( r.getResourceId(), tokens );
        if ( folder != null && !folder.isFolder() )
            throw new BuildingServerException( "Can't create a folder with the same name as a file that exists already." );
            
        if ( folder != null )
            {
            if ( !folder.isDeleted() )
                return folder.getSummary();
                //throw new BuildingServerException( "The folder already exists." );
            folder.setDeleted( false );
            folder.setUpdatedTime( now );
            folder.setUpdateUserId( getUserId() );
            folder.save();
            }
        else
            {
            log.debug( "Making record" );

            folder = new UploadedFile(UploadedFile.FOLDER);
            folder.setCreateUserId( getUserId() );
            folder.setUpdateUserId( getUserId() );
            folder.setName( (parent==null)?"":tokens[tokens.length-1] );
            try
            {
                UploadedFileManager manager = UploadedFileManager.getInstance(r);
                manager.addNode(parent, folder);
            }
            finally
            {
                UploadedFileManager.release(r);
            }
            folder.save();
            
            target = folder.getFile();
            if ( !target.exists()  )
            {
            if ( !target.mkdir() )
                throw new BuildingServerException( "Couldn't create folder in publishing area." );
            }
        
            }
            
        invalidateCache();
        return folder.getSummary();
        }
        
    private Resource getResourceWithCheck() throws BuildingServerException
    {
        Resource r = Resource.findResource( getResourceId());
        if ( r==null )
            throw new BuildingServerException( "Can't find resource for this session." );
        return r;
    }

    public void createFile( String pathname, long size, String mime_type )
        throws BuildingServerException
        {
        throw new BuildingServerException( "Requested functionality not yet implemented." );
        }

    public void checkCreateFolder( String pathname )
        throws BuildingServerException
        {
        throw new BuildingServerException( "Requested functionality not yet implemented." );
        }

    public void checkCreateFile( String pathname, long size )
        throws BuildingServerException
        {
        throw new BuildingServerException( "Requested functionality not yet implemented." );
        }
    
    public void deleteFile( String pathname )
    throws BuildingServerException
    {
        UploadedFile uploaded;
        Resource r = getResourceWithCheck();
        uploaded = UploadedFile.findUploadedFileByPath(r.getPrimaryKey(),
            pathname);
        if (uploaded == null)
            return;
        
        QuotaMetadatum quota = getQuotaUpdate();
        synchronized (quota.getLock())
        {
            try
            {
                UploadedFileManager.getInstance(r).delete(uploaded);
            }
            finally
            {
                UploadedFileManager.release(r);
            }
            quota.adjustUsage(-uploaded.getSize());
        }        
        invalidateCache();
    }

    public void undeleteFile( String pathname, boolean recurse )
        throws BuildingServerException
        {
        UploadedFile uploaded;
        Resource r = getResourceWithCheck();
        uploaded = UploadedFile.findUploadedFileByPath(r.getPrimaryKey(),
            pathname);
        if (uploaded == null)
            return;
        
        QuotaMetadatum quota = getQuotaUpdate();
        synchronized (quota.getLock())
        {
            checkQuota(getQuota(), uploaded.getSize());
            try
            {
                UploadedFileManager.getInstance(r).restore(uploaded, recurse);
            }
            finally
            {
                UploadedFileManager.release(r);
            }
            quota.adjustUsage(uploaded.getSize());
        }        
        invalidateCache();
        }
    
    public void renameFile( String pathname, String newname )
        throws BuildingServerException
        {
        if ( newname==null || newname.length()==0 )
            throw new BuildingServerUserException( "Empty names are not allowed." );
        if ( newname.indexOf( '/' )>=0 || newname.indexOf( '\\' )>=0  )
            throw new BuildingServerUserException( "File name with slashes are not allowed." );
        if ( newname.length()>255 )
            throw new BuildingServerUserException( "File names cannot exceed 255 chars in length." );
        
        Resource r = getResourceWithCheck();
        
        UploadedFile uf = UploadedFile.findUploadedFileByPath( r.getResourceId(), pathname );
        if ( uf == null )
            throw new BuildingServerUserException( "The selected file doesn't exist." );
        if ( uf.getParentUploadedFileId() == null )
            throw new BuildingServerUserException( "You can't rename the root folder in a resource." );
            
        UploadedFile parent = UploadedFile.findUploadedFile( uf.getParentUploadedFileId() );
        if ( parent == null )
            throw new BuildingServerException( "Can't find the folder for the specified file/folder." );
        
        UploadedFile destuf = UploadedFile.findUploadedFileByParentAndName( 
                    r.getResourceId(), parent.getUploadedFileId(), newname );
        
        // if the destination exists and is a different record to the source
        // then you can't rename because the name is in use
        if ( destuf!=null && !destuf.getUploadedFileId().equals( uf.getUploadedFileId() ) )
            throw new BuildingServerUserException( "A file already exists with the proposed name." );
        
        String oldname = uf.getName();
        File target =  uf.getFile();
        
        if (CSSScanner.shouldScan(newname))
        {
            Scanner scanner = new CSSScanner();
            ScannerResults results = scanner.scan(target);
            if ( ! results.isOk() )
            {
                throw new BuildingServerUserException(results.getMessages().toString());
            }
        }

        uf.setName( newname );
        File renamed = uf.getFile();
        

        
        if ( !target.renameTo( renamed ) )
            {
            uf.setName( oldname );
            uf.save();
            throw new BuildingServerException( "There was a problem renaming the file." );
            }
            
        uf.save();
        invalidateCache();
        }


    public void writeToFile( String pathname, long offset, byte[] data, int crc32 )
        throws BuildingServerException
        {
        throw new BuildingServerException( "Requested functionality not yet implemented." );
        }

    public void compareWithFile( String pathname, long offset, long length, int crc32 )
        throws BuildingServerException
        {
        throw new BuildingServerException( "Requested functionality not yet implemented." );
        }

    public int readFromFile( String pathname, long offset, byte[] data )
        throws BuildingServerException
        {
        throw new BuildingServerException( "Requested functionality not yet implemented." );
        }
        
    public String getPublishedURL( PrimaryKey uf_id )
        throws BuildingServerException
        {
        getResourceWithCheck();
        UploadedFile uf;
        
        if ( uf_id == null )
            return null;
        
        uf = UploadedFile.findUploadedFile( uf_id );
        if ( uf!=null )
            {
            return uf.getURLInResource();
            }
            
        return null;
        }

    public String getRealURL( PrimaryKey uf_id )
        throws BuildingServerException
        {
        getResourceWithCheck();
        UploadedFile uf;
        
        uf = UploadedFile.findUploadedFile( uf_id );
        if ( uf!=null && !uf.isDeleted() && !uf.isFolder() && uf.isComplete() )
            {
            return uf.getRealURLInResource();
            }
            
        return null;
        }
        
    public String getFileMimeType( String pathname )
        throws BuildingServerException
        {
        throw new BuildingServerException( "Requested functionality not yet implemented." );
        }

    public void setFileMimeType( String pathname, String mime_type )
        throws BuildingServerException
        {
        throw new BuildingServerException( "Requested functionality not yet implemented." );
        }

    public UploadedFileSummary getFileSummary( String pathname )
        throws BuildingServerException
        {
        Resource r = getResourceWithCheck();
        UploadedFile uf = UploadedFile.findUploadedFileByPath( r.getResourceId(), pathname );
        if ( uf == null )
            return null;
        return uf.getSummary();
        }

    public UploadedFileSummary getFileSummary( String[] path )
        throws BuildingServerException
        {
        Resource r = getResourceWithCheck();
        UploadedFile uf = UploadedFile.findUploadedFileByPath( r.getResourceId(), path );
        if ( uf == null )
            return null;
        return uf.getSummary();
        }
    
    public UploadedFileSummary getFileSummary( PrimaryKey id )
        throws BuildingServerException
        {
        Resource r = getResourceWithCheck();
        UploadedFile uf = UploadedFile.findUploadedFile( id );
        if ( uf == null )
            return null;
        // don't return data if the file belongs to another resource
        if ( uf.getResourceId().equals( r.getResourceId() ) )
            return uf.getSummary();
        return null;
        }

    public File getFile( PrimaryKey id )
        throws BuildingServerException
        {
        Resource r = getResourceWithCheck();
        UploadedFile uf = UploadedFile.findUploadedFile( id );
        if ( uf == null )
            return null;
        // don't return data if the file belongs to another resource
        if ( uf.getResourceId().equals( r.getResourceId() ) )
            return uf.getFile();
        return null;
        }
        
        
    public String getRealUrl( PrimaryKey uploaded_file_id )
        throws BuildingServerException
    {
    UploadedFile uf = UploadedFile.findUploadedFile( uploaded_file_id );
    return uf.getRealURLInResource();
    }

    public DefaultMutableTreeNode getFileAndDescendentSummaryTree( String pathname, boolean omit_deleted  )
        throws BuildingServerException
        {
        Resource r = getResourceWithCheck();
        Enumeration enumeration = UploadedFile.findUploadedFileDescendents( r.getResourceId(), pathname, omit_deleted );
            
        UploadedFile file, parent_file;
        UploadedFileNode root, parent, current;
        
        // root tree node is empty - all uploaded files are descendents of it
        root = new UploadedFileNode();
        parent = root;
        
        // if there isn't even a root folder put in an
        // unsaved one.
        if ( !enumeration.hasMoreElements() )
            {
            file = new UploadedFile();
            file.setParentUploadedFileId( null );
            file.setName( "" );
            file.setFolder( true );
            current = new UploadedFileNode();
            current.setUserObject( file );
            parent.add( current );
            return root;
            }
        
        while ( enumeration.hasMoreElements() )
            {
            file = (UploadedFile)enumeration.nextElement();
            current = new UploadedFileNode();
            current.setUserObject( file );
            
            // go up the tree to find the parent of this node
            // assumes that entries are sorted by left_index already
            // if parent isn't found it is added to the tree root 
            // (not the same as the file system root)
            parent_file = (UploadedFile)parent.getUserObject();
            while ( parent_file != null && 
                    !parent_file.getUploadedFileId().equals( file.getParentUploadedFileId() ) )
                {
                parent = (UploadedFileNode)parent.getParent();
                parent_file = (UploadedFile)parent.getUserObject();
                }
                
            parent.add( current );
            parent = current;
            }

            
        root.sortByName();
        
        return root;
        }


    public UploadedFileSummary[] getFileAndDescendentSummaries( String pathname, boolean omit_deleted  )
        throws BuildingServerException
        {
        try
            {
        DefaultMutableTreeNode root = getFileAndDescendentSummaryTree( pathname, omit_deleted  );
        DefaultMutableTreeNode node;
        
        Enumeration enumeration = root.preorderEnumeration();
        
        Vector list = new Vector();
        while ( enumeration.hasMoreElements() )
            {
            node = (DefaultMutableTreeNode)enumeration.nextElement();
            if ( node.getUserObject() != null )
                list.addElement( node.getUserObject() );
            }
        UploadedFileSummary[] summaries= new UploadedFileSummary[list.size()];
        for ( int i=0; i< list.size(); i++ )
            summaries[i] = ((UploadedFile)list.elementAt( i )).getSummary();

        return summaries;
            }
        catch ( Exception ex )
            {
            log.error( ex.getMessage(), ex );
            }
        return null;
        
        /*
        Resource r = getResourceWithCheck();
        Enumeration enumeration = UploadedFile.findUploadedFileDescendents( r.getResourceId(), pathname, omit_deleted );
        Vector list = new Vector();
        while ( enumeration.hasMoreElements() )
            list.addElement( enumeration.nextElement() );
        UploadedFileSummary[] summaries= new UploadedFileSummary[list.size()];
        for ( int i=0; i< list.size(); i++ )
            summaries[i] = ((UploadedFile)list.elementAt( i )).getSummary();
        return summaries;
        */
        }

    public UploadedFileSummary[] getFileAndAncestorSummaries( String pathname )
        throws BuildingServerException
        {
        Resource r = getResourceWithCheck();
        Enumeration enumeration = UploadedFile.findUploadedFileAncestors( r.getResourceId(), pathname );
        Vector list = new Vector();
        while ( enumeration.hasMoreElements() )
            list.addElement( enumeration.nextElement() );
        UploadedFileSummary[] summaries= new UploadedFileSummary[list.size()];
        for ( int i=0; i< list.size(); i++ )
            summaries[i] = ((UploadedFile)list.elementAt( i )).getSummary();
        return summaries;
        }
    
    public void transferZipFile( ZipFile archive, String startfilename )
    throws BuildingServerException
    {
    ZipEntry zipentry;
    String file_name, ext, mime_type;
    StringBuffer dest_filename = new StringBuffer();
    InputStream zipinput;
    FileOutputStream fout;
    File file;
    int b;
    
    try
        {
        for ( Enumeration enumeration=archive.entries(); enumeration.hasMoreElements(); )
            {
            zipentry = (ZipEntry)enumeration.nextElement();
            log.debug( "ZIP Entry = [" + zipentry + "]" );

            //ignore directories - don't create empty folders
            file_name = zipentry.getName();
            if ( zipentry.isDirectory() || file_name==null )
                continue;

            log.debug( "ZIP file = [" + file_name + "]" );

            dest_filename.setLength( 0 );
            dest_filename.append( startfilename );
            dest_filename.append( file_name  );


            zipinput = archive.getInputStream( zipentry );
            if ( zipinput==null )
                throw new BuildingServerException( "Unreadable ZIP file entry: " + zipentry.toString() );
                            
            file = BuildingContext.createTempFile( "zipentry", ".bin" );
            fout = new FileOutputStream( file );
            while ( (b=zipinput.read()) >= 0 )
                fout.write( b );
            fout.close();
            zipinput.close();

            if ( file_name.indexOf( '.' ) < 0 )
                ext = "bin";
            else
                ext = file_name.substring( file_name.lastIndexOf( '.' )+1 );
                       
            mime_type=BuildingContext.getProperty( "webpublish.mime.ext." + ext, "application/octet-stream" );
        
            transferFile( file.getAbsolutePath(), dest_filename.toString(), mime_type );
            }
        archive.close();
        (new File( archive.getName() )).delete();
        }
    catch ( Exception ex )
        {
        log.error( ex.getMessage(), ex );
        throw new BuildingServerException( "Problem unpacking package: " + ex.getMessage() );
        }
    }

    private void invalidateCache()
    {
        try
        {
            Resource r = getResourceWithCheck();
            File folder = r.getGeneratedFileFolder();
            File lfile = new File( folder, FILELISTING_XML );

            if ( lfile.exists() )
                lfile.delete();
        }
        catch ( Exception  ex )
        {
            log.error( ex.getMessage(), ex );
        }
        
        // BuildingSession caches uploaded files data.
        try
        {
            buildingSession.invalidateCache();
        }
        catch (BuildingServerException e)
        {
            log.error( e.getMessage(), e);
        }
    }
    
    public Quota getQuota() throws BuildingServerException 
    {
        final Permission permissions[] = new Permission[]{Permission.UPLOAD, Permission.MANAGE};
        if (ResourceUtils.anyPermission(getResourceWithCheck(), permissions))
        {
            QuotaMetadatum quota = getQuotaUpdate();
            return (quota.hasQuota())?quota:null;
        }
        throw new PermissionDeniedException(permissions);
    }
    
    public void setQuota(long quota) throws BuildingServerException
    {
        Resource resource = getResourceWithCheck();
        if (!resource.checkPermission(Permission.SYSADMIN))
            throw new PermissionDeniedException(Permission.SYSADMIN);
        
        getQuotaUpdate().setQuota(quota);        
    }
    
    /**
     * Gets the quota for the current resource that can be managed internally.
     */
    private QuotaMetadatum getQuotaUpdate() throws BuildingServerException
    {
        return QuotaFactory.getQuota(getResourceWithCheck(), QuotaFactory.FILES);
    }
    
    private PrimaryKey getResourceId() throws BuildingServerException
    {
        return buildingSession.getResourceId();
    }
    
    private PrimaryKey getUserId() throws BuildingServerException
    {
        return buildingSession.getUserId();
    }
}
