/* ======================================================================
   Parts Copyright 2006 University of Leeds, Oxford University, University of the Highlands and Islands.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

====================================================================== */

package org.bodington.servlet.facilities;

import org.apache.log4j.Logger;

import org.bodington.servlet.*;
//import java.security.*;
import java.io.*;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.Enumeration;
import java.util.Vector;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

import org.bodington.database.PrimaryKey;
import org.bodington.sqldatabase.SqlDatabase;
import org.bodington.server.*;
import org.bodington.server.realm.*;
import org.bodington.server.resources.Resource;
import org.bodington.server.events.Event;
import org.bodington.server.events.UserManagementEvent;
import org.bodington.text.BigString;
import org.bodington.text.Metadatum;
import org.bodington.util.BodingtonURL;
import org.bodington.util.DateFormatter;
import org.bodington.util.html.HtmlFilterFactory;

/**
 * @author Jon Maber
 * @author buckett
 * @author ningning
 */
public class UserDirectoryFacility extends org.bodington.servlet.facilities.AliasFacility
{
    
    private static Logger log = Logger.getLogger(UserDirectoryFacility.class);
    
    
    public void insert( Request req, PrintWriter out, String command, String insertname )
    throws ServletException, IOException
    {
        log.debug( " PasswordFacility insert()" );
            
        try
        {
            BuildingSession session;
            UserManagementSession um_session;
            
            session = BuildingSessionManagerImpl.getSession( req.getResource() );
            if ( !(session instanceof UserManagementSession) )
            {
                if (out != null)
                    out.println( "<HR>Technical problem: unable to access appropriate tool session.<HR>" );
                return;
            }
            um_session = (UserManagementSession)session;
            
            if ( command.equals( "confirmpass" ) )
            {
                if (out != null)
                    confirmpass(  req, out );
                return;
            }
            
            if ( command.equals( "usersearchform" ) )
            {
                if (out != null)
                    usersearchform(  req, out, insertname );
                return;
            }
            
            if ( command.equals( "listusers" ) )
            {
                if (out == null)
                    return;
                String output_format=req.getParameter( "output_format" );
                boolean xml_output = output_format != null && output_format.equalsIgnoreCase( "xml" );
                if ( xml_output )
                    out.println( "<span class=bs-textarea><TEXTAREA ROWS=20 COLS=80>" );
                listusers(  req, out, true );
                if ( xml_output )
                    out.println( "</TEXTAREA></span>" );
                return;
            }
            
            if ( command.equals( "userdata" ) )
            {
                if (out != null)
                    userdata(  req, out, insertname );
                return;
            }
            
            if ( command.equals( "confirmuserdata" ) )
            {
                if (out != null)
                    confirmuserdata( um_session, req, out );
                return;
            }
            
            if ( command.equals( "usereventlog" ) )
            {
                if (out != null)
                    usereventlog(  req, out );
                return;
            }
            
            if ( command.equals( "ifcanedituser" ) )
            {
                req.setSwitchedOff(  !ifcanedituser( req ) );
                return;
            }
            
            if ( command.equals( "removeallstudents" ) )
            {
                if (out != null)
                {
                    String message = um_session.removeAllStudentPasswords();
                    out.println( "<PRE>" );
                    out.println( message );
                    out.println( "</PRE>" );
                }
                return;
            }
            
            if ( command.equals( "resetallstudents" ) )
            {
                if (out != null)
                {
                    String message = um_session.resetAllStudentPasswords();
                    out.println( "<PRE>" );
                    out.println( message );
                    out.println( "</PRE>" );
                }
                return;
            }
            
            if ( command.equals( "resetnullstudents" ) )
            {
                if (out != null)
                {
                    String message = um_session.resetNullStudentPasswords();
                    out.println( "<PRE>" );
                    out.println( message );
                    out.println( "</PRE>" );
                }
                return;
            }
            
			if ( command.equals( "usergroups" ) )
            {
                if (out != null)
                    usergroups(  req, out );
                return;
            }
            
            if ( command.equals("usermonitor")) 
            {
                if (out != null)
            	    userMonitor(um_session,req,out);
                return;
            }
            
            if ( command.equals("authconfirm"))
            {
                if (out != null)
                    authenticationConfirm( req, out );
                return;
            }
            
        }
        catch ( BuildingServerException bsex )
        {
            out.println( bsex.toString() );
            return;
        }
        
        
        
        super.insert( req, out, command, insertname );
    }
    
    
    private void authenticationConfirm(Request req, PrintWriter out) throws BuildingServerException
    {
        // TODO Auto-generated method stub
        String auth = req.getServerNavigationSession().getAuthenticatorAlias();
        if ("standard_pass_phrase".equals(auth))
        {
            out.print(
                "<tr>"+
               "<td><input type=\"password\" name=\"password\"></td>"+
               "<td>Type <strong>YOUR</strong> current password in this box to"+
               "prove you have the right to reset this user\'s user name.</td>"+
               "</tr>");
        }
    }


    public void sendVirtualFile( Request req, HttpServletResponse res )
    throws ServletException, IOException
    {
        if ( req.getPageName().equals( "listusers.html" ) )
        {
            String output_format=req.getParameter( "output_format" );
            boolean xml_output = output_format != null && output_format.equalsIgnoreCase( "xml" );
            boolean html_output = !xml_output && !(output_format != null && output_format.equalsIgnoreCase( "text" ));
            PrintWriter out = new PrintWriter( res.getWriter() );
            res.setContentType( "text/plain" );

            if ( xml_output )
            {
                res.setContentType( "text/xml" );
            }
            else if ( html_output )
            {
                res.setContentType( "text/html" );
                out.println( "<HTML><HEAD>" );
                out.println( "<LINK HREF=\"bs_template_user.css\" TYPE=\"text/css\"  REL=\"STYLESHEET\">" );
                out.println( "</HEAD><BODY>" );
            }
            else
            {
                res.setContentType( "text/plain" );
            }


            listusers(  req, out, true );

            if ( html_output )
            {
                out.println( "</body></html>" );
            }

            return;
        }
        else
        {
            super.sendVirtualFile( req, res );
        }
    }
    
    private void confirmpass( Request req, PrintWriter out )
    throws IOException
    {
        String o, p, c;
        User user;
        PassPhrase pass;
        
        if ( !BuildingContext.getContext().checkPermission( Permission.EDIT ) )
        {
            out.println( "<HR><H4>You don't have permission to use this facility.  Sorry.</H4><HR>\n" );
            return;
        }
        
        o=req.getParameter( "oldpass" );
        p=req.getParameter( "pass" );
        c=req.getParameter( "confirm" );
        
        try
        {
            user = (User)BuildingContext.getContext().getUser();
            pass = PassPhrase.findPassPhrase( user );
            if ( pass==null )
            {
                out.println( "<HR><H4>There was a problem finding your user name and password data.</H4><HR>\n" );
                out.println( "<P>You can go <A HREF=bs_template_main.html>back</A> and try again.</P>" );
                return;
            }
            
            if ( !pass.isPassPhrase( o ) )
            {
                out.println( "<HR><H4>You didn't enter the correct current password and so you are not allowed to change it.</H4><HR>\n" );
                out.println( "<P>You can go <A HREF=bs_template_main.html>back</A> and try again.</P>" );
                return;
            }
            
            if ( p==null || c==null )
            {
                out.println( "<HR><H4>There was an unexpected problem processing input.  Sorry.</H4><HR>\n" );
                out.println( "<P>You can go <A HREF=bs_template_main.html>back</A> and try again.</P>" );
                return;
            }
            
            if ( p.length()==0 )
            {
                out.println( "<HR><H4>You didn't type anything in the new password box.</H4><HR>\n" );
                out.println( "<P>You can go <A HREF=bs_template_main.html>back</A> and try again.</P>" );
                return;
            }
            if ( p.length()<6 )
            {
                out.println( "<HR><H4>You didn't type a password that is long enough (must be 6 or more characters).</H4><HR>\n" );
                out.println( "<P>You can go <A HREF=bs_template_main.html>back</A> and try again.</P>" );
                return;
            }
            if ( c.length()==0 )
            {
                out.println( "<HR><H4>You didn't retype your new password in the second box.</H4><HR>\n" );
                out.println( "<P>You can go <A HREF=bs_template_main.html>back</A> and try again.</P>" );
                return;
            }
            if ( !c.equals(p) )
            {
                out.println( "<HR><H4>You typed different things in the two boxes.</H4><HR>\n" );
                out.println( "<P>You can go <A HREF=bs_template_main.html>back</A> and try again.</P>" );
                return;
            }
            
            pass.changePassPhrase( p );
            pass.save();
            
            UserManagementEvent event =
            new UserManagementEvent(
            UserManagementEvent.EVENT_PASSPHRASE_SET,
            req.getResource().getResourceId(),
            user.getUserId(),
            null,
            null,
            null,
            null,
            null );
            event.save();
            
            out.println(
            "<H4>Your new password has been stored.  " +
            "When you access another page in the building " +
            "you will be prompted to log in again.  You should use your " +
            "new password.</H4>" );
        }
        catch ( Exception ex )
        {
            out.println( "<HR><H4>There was a problem finding your user name and password data.</H4><HR>\n" );
            out.println( "<P>You can go <A HREF=bs_template_main.html>back</A> and try again.</P>" );
            return;
        }
    }
    
    
    private void userdata( Request req, PrintWriter out, String item )
    throws IOException
    {
        boolean canedit;
        User user, currentuser;
        UserDetail detail;
        
        
        try
        {
            currentuser = (User)BuildingContext.getContext().getUser();
            if ( currentuser == null )
            {
                out.println( "Technical problem - couldn't determine who you are." );
                return;
            }
            
            user = getUserFromUrl(req, currentuser);
            if ( user == null )
            {
                out.println( "<I>Unspecified user.</I>" );
                return;
            }
            
            // Can edit entry is current user has sysadmin access or if the
            // current user has manage access and the target user
            // belongs in the same adminsitrative zone or if the current user
            // is looking at their own entry and has been given edit level access
            // to the tool.
            canedit =
                isSysadminOrManageZone(req.getResource(), user, currentuser) ||
                isUserEditable(req.getResource(), user, currentuser);
            
            
            if ( item.equalsIgnoreCase( "button" ) && canedit )
            {
                out.println( "<INPUT TYPE=SUBMIT VALUE=Save>" );
                out.println( "<INPUT TYPE=HIDDEN NAME=op VALUE=userdata>" );
                return;
            }
            
            if ( item.equalsIgnoreCase( "user_id" ) )
            {
                out.println( user.getUserId().toString() );
                return;
            }
            
            if ( item.equalsIgnoreCase( "name" ) )
            {
                if ( canedit )
                    out.print( "<INPUT NAME=name VALUE=\"" );
                
                out.print( user.getName() );
                
                if ( canedit )
                    out.print( "\">" );
                return;
            }
            
            if ( item.equalsIgnoreCase( "surname" ) )
            {
                if ( canedit )
                    out.print( "<INPUT NAME=surname VALUE=\"" );
                
                out.println( user.getSurname() );
                
                if ( canedit )
                    out.print( "\">" );
                return;
            }
            
            if ( item.equalsIgnoreCase( "initials" ) )
            {
                if ( canedit )
                    out.print( "<INPUT NAME=initials VALUE=\"" );
                
                out.println( user.getInitials() );
                
                if ( canedit )
                    out.print( "\">" );
                return;
            }
            
            if ( item.equalsIgnoreCase( "user_name" ) )
            {
                PassPhrase pass = PassPhrase.findPassPhrase( user );
                if ( pass==null )
                {
                    out.println( "<I>You don't have a user name</I>" );
                    return;
                }
                out.println( pass.getUserName() );
                return;
            }
            
            if ( item.equalsIgnoreCase( "web_auth" ) )
            {
                WebAuthUser webAuthUser = WebAuthUser.findWebAuthUser( user );
                if ( webAuthUser==null )
                {
                    out.println( "<I>You don't have a WebAuth user name</I>" );
                    return;
                }
                out.println( webAuthUser.getUserName() );
                return;
            }
                        
            
            
            if ( item.equalsIgnoreCase( "email_address" ) )
            {
                detail = UserDetail.findUserDetail( "user_id = " + user.getUserId() );
                
                if ( canedit )
                    out.print( "<INPUT NAME=email_address VALUE=\"" );
                
                if (  detail !=null && detail.getEMailAddress() != null )
                    out.print( detail.getEMailAddress() );
                
                if ( canedit )
                    out.print( "\">" );
                return;
            }
            
            if ( item.equalsIgnoreCase( "htmlfilter") )
            {
                boolean filtered = HtmlFilterFactory.isFiltered(user);
                    out.print("<INPUT TYPE=checkbox NAME=htmlfilter");
                    if (filtered)
                        out.print(" CHECKED=true");
                    if ( !canedit )
                        out.print(" DISABLED=true");
                    out.print(">");
                    return;
            }
            
            if ( item.equalsIgnoreCase( "status" ) )
            {
                out.print("<INPUT TYPE=checkbox NAME=status");
                if (user.isValid())
                    out.print(" CHECKED=true");
                if ( !canedit )
                    out.print(" DISABLED=true");
                out.print(">");
                return;
            }
            
            if ( item.equalsIgnoreCase( "status_update" ) )
            {
                out.print("<INPUT TYPE=checkbox NAME=status_update");
                if (!user.isFixedStatus())
                    out.print(" CHECKED=true");
                if ( !canedit )
                    out.print(" DISABLED=true");
                out.print(">");
                return;
            }
            
            out.println( "<I>Unknown data item</I>" );
        }
        catch ( Exception ex )
        {
            out.println( "<HR><H4>There was a problem finding your personal data.</H4><HR>\n" );
            return;
        }
    }


    /**
     * Does the current user have sysadmin permission for the resource or
     * is the user beging changed in the same zone as the one doing the change
     * and the changer has manage rights.
     * @param resource The resource at which the check is happening.
     * @param user The user who is being changed.
     * @param currentuser The user who is doing the changing.
     * @return True if the change is allowed.
     */
    private boolean isSysadminOrManageZone(Resource resource, User user, User currentuser)
    {
        return resource.checkPermission(Permission.SYSADMIN)
            ||
            (resource.checkPermission(Permission.MANAGE) && user.getZoneId()
                .equals(currentuser.getZoneId()));
    }
    
    
    private void confirmuserdata( UserManagementSession um_session, Request req, PrintWriter out )
    throws IOException
    {
        String operation = req.getParameter( "op" );
        if ( operation==null )
            return;
        
        if ( operation.equalsIgnoreCase( "userdata" ) )
            saveuserdata( req, out );
        
        if ( operation.equalsIgnoreCase( "resetusername" ) )
            resetusername( um_session, req, out );
        
        if ( operation.equalsIgnoreCase( "resetpassword" ) )
            resetpassword( um_session, req, out );
        
        if ( operation.equalsIgnoreCase( "usermemo" ) )
            usermemo( req, out );
        
        if ( operation.equalsIgnoreCase( "addcertificate" ) )
            addcertificate( um_session, req, out );
        
        if ( operation.equalsIgnoreCase( "saveoptions" ) )
            saveOptions( um_session, req, out);
    }
    
    
    private void saveOptions(UserManagementSession session, Request req, PrintWriter out)
    {

        boolean canedit;
        User user=null, currentuser;
        
        
        try
        {
            currentuser = (User)BuildingContext.getContext().getUser();
            if ( currentuser == null )
            {
                out.println( "Technical problem - couldn't determine who you are." );
                return;
            }
            
            user = getUserFromUrl(req, currentuser);
            if ( user == null )
            {
                out.println( "<I>Unspecified user.</I>" );
                return;
            }
            canedit = 
                isSysadminOrManageZone(req.getResource(), user, currentuser);
            
            if ( !canedit )
            {
                out.println( "<P><FONT COLOR=RED>You don't have the right to change this user's options.</FONT></P>" );
                return;
            }
            Metadatum filterOption = Metadatum.findMetadatumByIdAndName(user.getPrimaryKey(), HtmlFilterFactory.META_FILTER);
            String oldValue = "unset";
            if (filterOption == null)
            {
                filterOption = new Metadatum();
                filterOption.setName(HtmlFilterFactory.META_FILTER);
                filterOption.setObjectId(user.getPrimaryKey());
            }
            else
            {
                oldValue = filterOption.getValue();
            }
            
            String filter = req.getParameter("htmlfilter");
            filterOption.setValue(Boolean.valueOf(filter != null).toString());
            
            boolean status = req.getParameter("status") != null;
            boolean statusUpdate = req.getParameter("status_update") != null;
            int statusExpired = user.getStatusExpired();
            
            if (status)
            {
                if (statusUpdate)
                    user.setStatusExpired( User.VALID);
                else
                    user.setStatusExpired(User.ALWAYS_VALID);
            }
            else
            {
                if (statusUpdate)
                    user.setStatusExpired(User.EXPIRED);
                else
                    user.setStatusExpired(User.ALWAYS_EXPIRED);
            }
            
            boolean changed = false;
            if (user.isUnsaved())
            {
                user.save();
                UserManagementEvent event = new UserManagementEvent(
                    UserManagementEvent.EVENT_DATA_CHANGE,
                    req.getResource().getResourceId(),
                    currentuser.getUserId(), user.getUserId(), null,
                    "Expiry Status", (String)null, new Integer(user.getStatusExpired()).toString());
                event.save();
                changed = true;
            }
            
            
            if (!oldValue.equals(filterOption.getValue()))
            {
                filterOption.save();
                UserManagementEvent event = new UserManagementEvent(
                    UserManagementEvent.EVENT_HTMLFILTER_CHANGE, req
                        .getResource().getResourceId(),
                    currentuser.getUserId(), currentuser.getUserId().equals(
                        user.getUserId()) ? null : user.getUserId(), null,
                    "HTML Filter", oldValue, filterOption.getValue());
                event.save();
                changed = true;
            }

            if (changed)
            {
                out.println( "<P><FONT COLOR=RED>Options Saved.</FONT></P>" );
            }
            else
            {
                out.println("<P><FONT COLOR=RED>Options not changed.</FONT></P>");  
            }
            
        }
        catch ( Exception ex )
        {
            log.error(ex);
            out.println( "<p><strong>There was a problem saving the options.</strong></p>" );
            return;
        }

    }


    private void addcertificate( UserManagementSession um_session, Request req, PrintWriter out )
    throws IOException
    {
        boolean canedit;
        String item, message=null;
        String cert_file_name;
        File cert_file;
        User user=null, currentuser;
        
        try
        {
            currentuser = (User)BuildingContext.getContext().getUser();
            if ( currentuser == null )
            {
                out.println( "Technical problem - couldn't determine who you are." );
                return;
            }
            
            user = getUserFromUrl(req, currentuser);
            if ( user == null )
            {
                out.println( "<I>Unspecified user.</I>" );
                return;
            }
            
            canedit = req.getResource().checkPermission( Permission.SYSADMIN );
            
            if ( !canedit )
            {
                out.println( "<P><FONT COLOR=RED>You don't have the right to add a certificate to this user's account.</FONT></P>" );
                return;
            }
            
            PassPhrase currentpass = PassPhrase.findPassPhrase( currentuser );
            
            item = req.getParameter( "password" );
            if ( item==null || item.length()<1 )
            {
                out.println( "<P><FONT COLOR=RED>You must type your password into the box.</FONT></P>" );
                return;
            }
            
            Thread.sleep( 2000 );
            if ( !currentpass.isPassPhrase( item ) )
            {
                out.println( "<P><FONT COLOR=RED>The password you entered was not correct.</FONT></P>" );
                return;
            }
            
            cert_file_name = req.getParameterFileLocation( "file" );
            if ( cert_file_name==null || cert_file_name.length()<1 )
            {
                out.println( "<p><strong>You must seeclt a certificate file to upload.</strong></p>" );
                return;
            }
            cert_file = new File( cert_file_name );
            if ( !cert_file.exists() )
            {
                out.println( "<p><strong>The selected file couldn't be found.</strong> ( " + cert_file_name + ")</p>" );
                return;
            }
            
            message = um_session.addUserCertificate( user.getUserId(), cert_file );
            out.print( "<P><FONT COLOR=RED><PRE>" );
            out.print( message );
            out.println( "</PRE></FONT></P>" );

        }
        catch ( Exception ex )
        {
            logException( null, "UserDirectoryFacility", "addcertificate",
            "There was a problem adding the certificate.",
            ex );
            out.println( "<p><strong>There was a problem adding the certificate. Details may appear in the system error logs.</strong></p>" );
            return;
        }
    }


    /**
     * Users can be encoded into a URL and this method attempts to find the user.
     * @param req The request containing a user ID in the URL.
     * @param currentuser The default user to return.
     * @return The found User.
     * @throws BuildingServerException If we can't look for the user in the store.
     */
    private User getUserFromUrl(Request req, User currentuser) throws BuildingServerException
    {
        String u;
        PrimaryKey uid;
        User user = currentuser;
        if ( req.getTemplateParameterCount()!= 0)
        {
            uid = null;
            user = null;
            u = (String)req.getTemplateParameter( 0 );
            try
            {
                if ( u!=null )
                    uid = new PrimaryKey( Integer.parseInt( u ) );
            }
            catch ( NumberFormatException nfex )
            {
                uid = null;
            }
            if ( uid!=null )
                user = User.findUser( uid );
        }
        return user;
    }
    
    
    private void resetusername( UserManagementSession um_session, Request req, PrintWriter out )
    throws IOException
    {
        boolean canedit;
        String item, user_name=null, message=null;
        User user=null, currentuser;
        PassPhrase pass=null, currentpass;
        
        
        try
        {
            currentuser = (User)BuildingContext.getContext().getUser();
            if ( currentuser == null )
            {
                out.println( "Technical problem - couldn't determine who you are." );
                return;
            }
            
            
            user = getUserFromUrl(req, currentuser);
            if ( user == null )
            {
                out.println( "<I>Unspecified user.</I>" );
                return;
            }
            
            // Can edit entry is current user has sysadmin or manage access and
            // belongs to same zone
            // users can't reset their own user name
            canedit = 
                isSysadminOrManageZone(req.getResource(), user, currentuser);
            if ( !canedit )
            {
                out.println( "<P><FONT COLOR=RED>You don't have the right to reset this user's password.</FONT></P>" );
                return;
            }
            
            currentpass = PassPhrase.findPassPhrase( currentuser );
            pass = PassPhrase.findPassPhrase( user );
            
            item = req.getParameter( "password" );
            
            Thread.sleep( 2000 );
            if ( currentpass != null && !currentpass.isPassPhrase( item ) )
            {
                out.println( "<P><FONT COLOR=RED>The password you entered was not correct.</FONT></P>" );
                return;
            }
            
            if ( pass==null )
                user_name = null;
            else
                user_name = pass.getUserName();
            
            message=null;
            message = um_session.resetUserName( user.getUserId() );
            out.print( "<P><FONT COLOR=RED><PRE>" );
            out.print( message );
            out.println( "</PRE></FONT></P>" );
            
            pass = PassPhrase.findPassPhrase( user );
            UserManagementEvent event =
            new UserManagementEvent(
            UserManagementEvent.EVENT_DATA_CHANGE,
            req.getResource().getResourceId(),
            currentuser.getUserId(),
            currentuser.getUserId().equals( user.getUserId() )?null:user.getUserId(),
            null,
            "user name",
            user_name,
            pass.getUserName() );
            event.save();
        }
        catch ( Exception ex )
        {
            logException( null, "UserDirectoryFacility", "resetusername",
            "There was a problem saving personal data.",
            ex );
            return;
        }
    }
    
    
    private void resetpassword( UserManagementSession um_session, Request req, PrintWriter out )
    throws IOException
    {
        boolean canedit;
        String item, oldpass=null, message=null;
        User user=null, currentuser;
        PassPhrase pass=null, currentpass;
        
        
        try
        {
            currentuser = (User)BuildingContext.getContext().getUser();
            if ( currentuser == null )
            {
                out.println( "Technical problem - couldn't determine who you are." );
                return;
            }
            
            
            user = getUserFromUrl(req, currentuser);
            if ( user == null )
            {
                out.println( "<I>Unspecified user.</I>" );
                return;
            }
            
            // Can edit entry is current user has sysadmin access or if the
            // current user has manage access and the target user
            // belongs in the same adminsitrative zone or if the current user
            // is looking at their own entry and has been given edit level access
            // to the tool.
            canedit = 
                isSysadminOrManageZone(req.getResource(), user, currentuser) ||
                isUserEditable(req.getResource(), user, currentuser);
            if ( !canedit )
            {
                out.println( "<P><FONT COLOR=RED>You don't have the right to reset this user's password.</FONT></P>" );
                return;
            }
            
            currentpass = PassPhrase.findPassPhrase( currentuser );
            pass = PassPhrase.findPassPhrase( user );
            
            if ( pass == null )
            {
                out.println( "<P><FONT COLOR=RED>Couldn't find the user's pass phrase record.</FONT></P>" );
                return;
            }
            
            item = req.getParameter( "password" );
            Thread.sleep( 2000 );
            if ( currentpass != null && !currentpass.isPassPhrase( item ) )
            {
                out.println( "<P><FONT COLOR=RED>The password you entered was not correct.</FONT></P>" );
                return;
            }
            
            oldpass = pass.getPassPhrase();
            message=null;
            message = um_session.resetUserPassword( user.getUserId() );
            out.print( "<P><FONT COLOR=RED><PRE>" );
            out.print( message );
            out.println( "</PRE></FONT></P>" );
            
            UserManagementEvent event =
            new UserManagementEvent(
            UserManagementEvent.EVENT_PASSPHRASE_RESET,
            req.getResource().getResourceId(),
            currentuser.getUserId(),
            currentuser.getUserId().equals( user.getUserId() )?null:user.getUserId(),
            null,
            null,
            null,
            null );
            event.save();
        }
        catch ( Exception ex )
        {
            logException( null, "UserDirectoryFacility", "resetpassword",
            "There was a problem saving personal data.",
            ex );
            if ( oldpass!=null && message==null )
            {
                try
                {
                    // This is ugly but going through the session means an encrypted
                    // password get re-encrypted.
                    pass.setPassPhrase( oldpass );
                    pass.save();
                }
                catch ( BuildingServerException bsex )
                {
                    logException( null, "UserDirectoryFacility", "resetpassword",
                    "There was another problem trying to set the password back to its old value.",
                    bsex );
                }
            }
            return;
        }
    }
    
    
    private void saveuserdata( Request req, PrintWriter out )
    throws IOException
    {
        boolean canedit;
        int i, j;
        char c;
        String item, old_item, initials;
        User user, currentuser;
        UserManagementEvent event;
        
        try
        {
            currentuser = (User)BuildingContext.getContext().getUser();
            if ( currentuser == null )
            {
                out.println( "Technical problem - couldn't determine who you are." );
                return;
            }
            
            
            user = getUserFromUrl(req, currentuser);
            if ( user == null )
            {
                out.println( "<I>Unspecified user.</I>" );
                return;
            }
            
            // Can edit entry is current user has sysadmin access or if the
            // current user has manage access and the target user
            // belongs in the same adminsitrative zone or if the current user
            // is looking at their own entry and has been given edit level access
            // to the tool.
            canedit =
                isSysadminOrManageZone(req.getResource(), user, currentuser) ||
                isUserEditable(req.getResource(), user, currentuser);
            if ( !canedit )
            {
                out.println( "<P><FONT COLOR=RED>You don't have the right to edit this user's personal data.</FONT></P>" );
                return;
            }
            
            // Added email address as quick hack; method ideally needs to be re-written:
            String[] fields =
            { "name", "surname", "initials", "email_address"};
            
            
            for ( j=0; j< fields.length; j++ )
            {
                item = req.getParameter( fields[j] );
                if ( item == null )
                    continue;
                
                item = item.trim();
                
                if ( j==2 )
                {
                    initials = "";
                    for ( i=0; i<item.length(); i++ )
                    {
                        c=item.charAt(i);
                        if ( Character.isLetter( c ) )
                            initials = initials + item.substring( i, i+1 ).toUpperCase();
                    }
                    if ( initials.length()==0 )
                    {
                        out.println( "<P><FONT COLOR=RED>The initials field wasn't saved because it didn't contain any letters.</FONT></P>" );
                        continue;
                    }
                    item = initials;
                }
                
                if ( j!= 3 && item.length()<1 )
                {
                    out.print( "<P><FONT COLOR=RED>The " );
                    out.print( fields[j] );
                    out.println( " field wasn't saved because you can't leave this field empty.</FONT></P>" );
                    continue;
                }
                if ( (j==0 || j==1) && item.length()>64 )
                {
                    out.print( "<P><FONT COLOR=RED>The " );
                    out.print( fields[j] );
                    out.println( " field was saved but it was truncated because it exceeded 64 characters.</FONT></P>" );
                    item = item.substring( 0, 64 );
                }
                if ( (j==2) && item.length()>8 )
                {
                    out.print( "<P><FONT COLOR=RED>The " );
                    out.print( fields[j] );
                    out.println( " field was saved but it was truncated because it exceeded 8 characters.</FONT></P>" );
                    item = item.substring( 0, 8 );
                }
                
                if ( j!= 3)
                {
                    for ( i=0; i<item.length(); i++ )
                    {
                        c=item.charAt(i);
                        if ( !Character.isLetterOrDigit( c ) &&
                                        c != '_'  &&
                                        c != '-'  &&
                                        c != ' '  &&
                                        c != '\'' &&
                                        c != '.'       )
                        {
                            out.print( "<P><FONT COLOR=RED>The " );
                            out.print( fields[j] );
                            out.println( " field wasn't saved because names can only contain letters digits, '-', '_' or a space.</FONT></P>" );
                            break;
                        }
                    }

                    if ( i<item.length() )
                        continue;
                }
                switch ( j )
                {
                    case 0:
                        old_item = user.getName();
                        if ( !item.equals( old_item ) )
                        {
                            user.setName( item );
                            user.save();
                            out.println( "<P><FONT COLOR=RED>Name was saved.</FONT></P>" );
                            event =
                            new UserManagementEvent(
                            UserManagementEvent.EVENT_DATA_CHANGE,
                            req.getResource().getResourceId(),
                            currentuser.getUserId(),
                            currentuser.getUserId().equals( user.getUserId() )?null:user.getUserId(),
                            null,
                            "name",
                            old_item,
                            item );
                            event.save();
                        }
                        break;
                    case 1:
                        old_item = user.getSurname();
                        if ( !item.equals( old_item ) )
                        {
                            user.setSurname( item );
                            user.save();
                            out.println( "<P><FONT COLOR=RED>Surname was saved.</FONT></P>" );
                            event =
                            new UserManagementEvent(
                            UserManagementEvent.EVENT_DATA_CHANGE,
                            req.getResource().getResourceId(),
                            currentuser.getUserId(),
                            currentuser.getUserId().equals( user.getUserId() )?null:user.getUserId(),
                            null,
                            "surname",
                            old_item,
                            item );
                            event.save();
                        }
                        break;
                    case 2:
                        old_item = user.getInitials();
                        if ( !item.equals( old_item ) )
                        {
                            user.setInitials( item );
                            user.save();
                            out.println( "<P><FONT COLOR=RED>Initials were saved.</FONT></P>" );
                            event =
                            new UserManagementEvent(
                            UserManagementEvent.EVENT_DATA_CHANGE,
                            req.getResource().getResourceId(),
                            currentuser.getUserId(),
                            currentuser.getUserId().equals( user.getUserId() )?null:user.getUserId(),
                            null,
                            "initials",
                            old_item,
                            item );
                            event.save();
                        }
                        break;
                    case 3:
                        UserDetail details = UserDetail.findUserDetail( user );
                        if ( details == null )
                        {
                            details = new UserDetail();
                            details.setUser( user );
                        }
                        
                        old_item = details.getEMailAddress();
                        
                        if ( !item.equals( old_item ) )
                        {
                            details.setEMailAddress( item );
                            details.save();
                            out.println( "<P><FONT COLOR=RED>Email address saved.</FONT></P>" );
                            event =
                            new UserManagementEvent(
                            UserManagementEvent.EVENT_DATA_CHANGE,
                            req.getResource().getResourceId(),
                            currentuser.getUserId(),
                            currentuser.getUserId().equals( user.getUserId() )?null:user.getUserId(),
                            null,
                            "email_address",
                            old_item,
                            item );
                            event.save();
                        }
                        break;
                }
            }
            
        }
        catch ( Exception ex )
        {
            out.println( "<HR><H4>There was a problem saving personal data.</H4><HR>\n" );
            log.error( ex );
            return;
        }
    }
    
    private void usermemo( Request req, PrintWriter out )
    throws IOException
    {
        boolean canedit;
        String memo;
        User user, currentuser;
        UserManagementEvent event;
        
        try
        {
            currentuser = (User)BuildingContext.getContext().getUser();
            if ( currentuser == null )
            {
                out.println( "Technical problem - couldn't determine who you are." );
                return;
            }
            
            
            user = getUserFromUrl(req, currentuser);
            if ( user == null )
            {
                out.println( "<I>Unspecified user.</I>" );
                return;
            }
            
            // Can edit entry is current user has sysadmin access or if the
            // current user has manage access and the target user
            // belongs in the same adminsitrative zone or if the current user
            // is looking at their own entry and has been given edit level access
            // to the tool.
            canedit =
                isSysadminOrManageZone(req.getResource(), user, currentuser);
            
            if ( !canedit )
            {
                out.println( "<P><FONT COLOR=RED>You don't have the right to record memos against this user record.</FONT></P>" );
                return;
            }
            
            memo = req.getParameter( "memo" );
            if ( memo !=null )
                memo = memo.trim();
            if ( memo==null || memo.length()==0 )
            {
                out.println( "<P><FONT COLOR=RED>The memo wasn't recorded because the text was empty.</FONT></P>" );
                return;
            }
            
            BigString big = new BigString();
            big.setString( memo );
            big.save();
            
            event = new UserManagementEvent(
            UserManagementEvent.EVENT_MEMO,
            req.getResource().getResourceId(),
            currentuser.getUserId(),
            currentuser.getUserId().equals( user.getUserId() )?null:user.getUserId(),
            big.getBigStringId(),
            null,
            null,
            null );
            event.save();
            
            out.println( "<P><FONT COLOR=RED>Memo was recorded.</FONT></P>" );
        }
        catch ( Exception ex )
        {
            out.println( "<HR><H4>There was a problem recording the memo.</H4><HR>\n" );
            return;
        }
    }
    
    
    public void usereventlog(  Request req, PrintWriter out )
    throws IOException
    {
        boolean canedit;
        int j;
        Event event;
        StringBuffer where;
        User user, currentuser;
        
        
        if ( out==null )
            return;
        
        if ( !BuildingContext.getContext().checkPermission( Permission.MANAGE ) )
        {
            out.println( "<HR>You need manage access to view the log.<HR>" );
            return;
        }
        
        try
        {
            currentuser = (User)BuildingContext.getContext().getUser();
            if ( currentuser == null )
            {
                out.println( "Technical problem - couldn't determine who you are." );
                return;
            }
            
            
            user = getUserFromUrl(req, currentuser);
            if ( user == null )
            {
                out.println( "<I>Unspecified user.</I>" );
                return;
            }
            
            // Can edit entry is current user has sysadmin access or if the
            // current user has manage access and the target user
            // belongs in the same adminsitrative zone or if the current user
            // is looking at their own entry and has been given edit level access
            // to the tool.
            canedit =
                isSysadminOrManageZone(req.getResource(), user, currentuser);
            
            if ( !canedit )
            {
                out.println( "<P><FONT COLOR=RED>You don't have the right to record memos against this user record.</FONT></P>" );
                return;
            }
            
            where = new StringBuffer();
            where.append( "type = 24 AND passive_user_id = " );
            where.append( user.getUserId().toString() );
            
            Enumeration enumeration = Event.findEvents( where.toString(), "event_time" );
            
            PrintWriter writer = new PrintWriter( out );
            for ( j=0; enumeration.hasMoreElements();  )
            {
                event=(Event)enumeration.nextElement();
                
                if ( j==0 )
                    out.println( "</A></H3><TABLE CLASS=bs-table-opaque>" );
                
                out.println( "<TR><TD><I>" );
                out.print( DateFormatter.formatDate( event.getEventTime(), DateFormatter.DEFAULT ) );
                out.println( "</I></TD><TD>" );
                event.printMessage( writer, true );
                writer.flush();
                out.println( "</TD></TR>" );
                j++;
            }
            if ( j>0 )
                out.println( "</TABLE>" );
            else
                out.println( "<P>There are no events in the event log here.</P>" );
        }
        catch ( Exception ex )
        {
            out.println( "</TD></TR></TABLE><HR>There was a technical problem trying to get a list of events.<HR>" );
            out.println( ex.toString() );
            return;
        }
        
    }
    
    
    
    private void usersearchform( Request req, PrintWriter out, String item )
    throws IOException
    {
        
        try
        {
            out.println( "<FORM METHOD=POST ACTION=bs_virtual_listusers.html>" );
            
            userSearchFields( req, out, item.equalsIgnoreCase("advanced"), true );
            
            out.println( "</FORM>" );
        }
        catch ( Exception ex )
        {
            out.println( "<HR><H4>There was a problem finding your users.</H4><HR>\n" );
            return;
        }
    }
    
    
    private boolean ifcanedituser( Request req )
    throws IOException
    {
        boolean canedit;
        User user, currentuser;
        
        try
        {
            currentuser = (User)BuildingContext.getContext().getUser();
            if ( currentuser == null )
                return false;
            
            
            user = getUserFromUrl(req, currentuser);
            if ( user == null )
            {
                return false;
            }
            
            // Can edit entry is current user has sysadmin access or if the
            // current user has manage access and the target user
            // belongs in the same adminsitrative zone or if the current user
            // is looking at their own entry and has been given edit level access
            // to the tool.
            canedit = 
                isSysadminOrManageZone(req.getResource(), user, currentuser) ||
                isUserEditable(req.getResource(), user, currentuser);
        }
        catch ( Exception ex )
        {
            return false;
        }
        
        return canedit;
    }


    /**
     * Check if the User is editable by the supplied user.
     * It checks that the user has edit permissions for the resource and the
     * two users are the same.
     * @param resource The resource in which the edit is happening.
     * @param user The user who is being edited.
     * @param currentuser The user who is doing the editing.
     * @return True if the edit can happen.
     */
    private boolean isUserEditable(Resource resource, User user, User currentuser)
    {
        return ( resource.checkPermission( Permission.EDIT ) &&
        user.getUserId().equals(currentuser.getUserId())      );
    }
    
    /**
     * List all user group memberships, including the special group memberships.
     * @throws IOException
     */
    public void usergroups(  Request req, PrintWriter out )
    throws IOException
    {  
        try
        {  
            User user = getUserFromUrl(req, (User)req.getUserPrincipal());
            out.println("<TABLE CLASS=bs-table-opaque WIDTH=100%>");
            out.println("<TR><TD CLASS=bs-cell-special COLSPAN=2><strong>User Group Memberships</strong></TD></TR>");
            
            //list regular groups memberships
            Enumeration groups = Group.findGroups("name NOT LIKE 'localgroup%' AND group_id IN (SELECT group_id FROM members WHERE user_id = "+ user.getPrimaryKey()+ ")", "name");
            if (!groups.hasMoreElements())
                out.println("<TR><TD CLASS=bs-cell-special COLSPAN=2>No group membership found</TD></TR>");
            
            while(groups.hasMoreElements())
            {       
        	    Group group = (Group)groups.nextElement();
        	    out.println("<TR><TD>Regular Group Member: </TD><TD>" + group.getName() + "</TD></TR>");    	
        	}

            Enumeration specialGroups = Group.findGroups("special_group > 0", "name");
        	while(specialGroups.hasMoreElements())
        	{
        		Group group = (Group)specialGroups.nextElement();
        		if (user.isMember(group))
        		{
        		    out.println("<TR><TD>Special Group Member: </TD><TD>" + group.getName() + "</TD></TR>");
        		}
        	}

            out.println("</TABLE>");
        }
        catch ( Exception ex )
        {  
            out.println( "</TD></TR></TABLE><HR>There was a technical problem trying to get a list of groups.<HR>" );
            out.println( ex.toString() );

            return;
        }
    }
 
    /**
     * Gives user monitoring information.
     */
    public void userMonitor(UserManagementSession um_session,Request req, PrintWriter out ) 
        throws BuildingServerException
    {
        Iterator sessions = HttpSession.getAllSessions();
        
    	//filter and sort the sessions by the session creation time.
    	ArrayList sessionList = new ArrayList(); 
    	while(sessions.hasNext())
    	{
    		HttpSession session = (HttpSession)sessions.next();
			if (session.getUserId() != null)
    		    sessionList.add(session);
    	}
    	int activateCount = sessionList.size();
    	
    	Comparator comp = new Comparator()
		{
    		public int compare(Object o1,Object o2)
    		{
    			HttpSession session1 = (HttpSession)o1;
    			HttpSession session2 = (HttpSession)o2;
    			
    			if (session1.getCreationTime() > session2.getCreationTime())
    				return 1;
				else
					return -1;
    		}
		};
		Collections.sort(sessionList,comp);    	
		sessions = sessionList.iterator();
    	
    	//output the user sessions info.   	
    	out.println("Refreshed Time: " +DateFormatter.formatDate(new Date(), DateFormatter.MEDIUM)+ "<br>");
    	out.println("Total Activate Session Count: " + activateCount);    	
    	out.println("<table class='bs-table-opaque'>");
    	out.println("<tr>" +
    			"<th>No</th>" +
    			"<th>User Id</th>" +
    			"<th>Name</th>" +
				"<th>SessionCreationTime</th>"+
				"<th>LastAccessedTime</th>"+
                "<th>Idle Timeout</th>"+
				"<th>VLE Location</th>"+
				"<th>RemoteHost</th>"+
				"</tr>");
    	    	
    	int n = 1;
    	BodingtonURL bodingtonUrl = new BodingtonURL(req);    	
    	
    	while(sessions.hasNext())
    	{
    		out.println("<tr>");
    		out.println("<td>" + (n++) + "</td>");
    	    
    	    HttpSession session  = (HttpSession)sessions.next();    	    
    	    PrimaryKey userId = session.getUserId();
    	    out.println("<td>" + userId + "</td>");
    	    
    	    User user = User.findUser(userId);    	    
    	    out.println("<td>" + user.getName() + "</td>");    	    
    	    long creationTime = session.getCreationTime();
    	    out.println("<td>" + DateFormatter.formatDate(new java.util.Date(creationTime), DateFormatter.MEDIUM) + "</td>");
    	    long lastAccessTime = session.getLastAccessedTime();
    	    out.println("<td>" + DateFormatter.formatDate(new java.util.Date(lastAccessTime), DateFormatter.MEDIUM) + "</td>");
            out.println("<td>"+ session.getMaxInactiveInterval()/60+ ":"+ new DecimalFormat("00").format(session.getMaxInactiveInterval()%60)+"</td>");
    	        	    
            out.print("<td>");
            Object attribute = session.getAttribute(Request.RESOURCE_ATTRIBUTE);
    	    if (attribute instanceof PrimaryKey)
            {
                PrimaryKey pk = (PrimaryKey) attribute;
                String location = "";
    	    	try
				{
    	    	    location = bodingtonUrl.getResourceUrl(pk,false);
                    out.print("<a href='"+location+"'>"+location+"</a>");
				} 
                catch(Exception e)
				{
					log.warn(e);
				}
    	    }
    	    out.println("</td>");
    	    String remoteHost = session.getRemoteHost();
    	    out.println("<td>" + remoteHost + "</td>");
    	    
    	    out.println("</tr>");
    	}
    	out.println("</table>");
    }
    
    public boolean isSysadmin()
    {
        return true;
    }
}
