/*
 * Copyright 2000, International Business Machines Corporation and others.
 * All Rights Reserved.
 * 
 * This software has been released under the terms of the IBM Public
 * License.  For details, see the LICENSE file in the top-level source
 * directory or online at http://www.openafs.org/dl/license10.html
 */

#include <afs/param.h>
#include <afs/stds.h>

#ifndef DJGPP
#include <windows.h>
#endif /* !DJGPP */
#include <malloc.h>
#include <string.h>

#include <osi.h>
#include <rx/rx.h>

#include "afsd.h"

osi_rwlock_t cm_userLock;

cm_user_t *cm_rootUserp;

void cm_InitUser(void)
{
	static osi_once_t once;
        
        if (osi_Once(&once)) {
		lock_InitializeRWLock(&cm_userLock, "cm_userLock");
                osi_EndOnce(&once);
	}
        
        cm_rootUserp = cm_NewUser();
}

cm_user_t *cm_NewUser(void)
{
	cm_user_t *up;
        
        up = malloc(sizeof(*up));
        memset(up, 0, sizeof(*up));
        up->refCount = 1;
        up->vcRefs = 1;		/* from caller */
        lock_InitializeMutex(&up->mx, "cm_user_t");
        return up;
}

/* must be called with locked userp */
cm_ucell_t *cm_GetUCell(cm_user_t *userp, cm_cell_t *cellp)
{
	cm_ucell_t *ucp;
        
	lock_AssertMutex(&userp->mx);
        for(ucp = userp->cellInfop; ucp; ucp=ucp->nextp) {
		if (ucp->cellp == cellp) break;
        }
        
        if (!ucp) {
		ucp = malloc(sizeof(*ucp));
                memset(ucp, 0, sizeof(*ucp));
                ucp->nextp = userp->cellInfop;
		if (userp->cellInfop)
			ucp->iterator = userp->cellInfop->iterator + 1;
		else
			ucp->iterator = 1;
                userp->cellInfop = ucp;
                ucp->cellp = cellp;
        }
        
        return ucp;
}

cm_ucell_t *cm_FindUCell(cm_user_t *userp, int iterator)
{
	cm_ucell_t *ucp;
	cm_ucell_t *best;

	best = NULL;
	lock_AssertMutex(&userp->mx);
	for (ucp = userp->cellInfop; ucp; ucp = ucp->nextp) {
		if (ucp->iterator >= iterator)
			best = ucp;
		else
			break;
	}
	return best;
}

void cm_HoldUser(cm_user_t *up)
{
	lock_ObtainWrite(&cm_userLock);
	up->refCount++;
	lock_ReleaseWrite(&cm_userLock);
}

void cm_ReleaseUser(cm_user_t *up)
{
	cm_ucell_t *ucp;
    cm_ucell_t *ncp;

	if (up == NULL) return;

	lock_ObtainWrite(&cm_userLock);
	osi_assert(up->refCount-- > 0);
	if (up->refCount == 0) {
		lock_FinalizeMutex(&up->mx);
        for(ucp = up->cellInfop; ucp; ucp = ncp) {
			ncp = ucp->nextp;
			if (ucp->ticketp) free(ucp->ticketp);
            free(ucp);
        }
        free(up);
    }
	lock_ReleaseWrite(&cm_userLock);
}

/* release the count of the # of connections that use this user structure.
 * When this hits zero, we know we won't be getting an new requests from
 * this user, and thus we can start GC'ing connections.  Ref count on user
 * won't hit zero until all cm_conn_t's have been GC'd, since they hold
 * refCount references to userp.
 */
void cm_ReleaseUserVCRef(cm_user_t *userp)
{
	lock_ObtainMutex(&userp->mx);
	osi_assert(userp->vcRefs-- > 0);
	lock_ReleaseMutex(&userp->mx);
}


/*
 * Check if any users' tokens have expired and if they have then do the 
 * equivalent of unlogging the user for that particular cell for which 
 * the tokens have expired.
 * ref. cm_IoctlDelToken() in cm_ioctl.c 
 * This routine is called by the cm_Daemon() ie. the periodic daemon.
 * every cm_daemonTokenCheckInterval seconds 
 */
void cm_CheckTokenCache(long now)
{
        extern smb_vc_t *smb_allVCsp; /* global vcp list */
	smb_vc_t   *vcp;
	smb_user_t *usersp;
	cm_user_t  *userp = NULL;
	cm_ucell_t *ucellp;
	BOOL bExpired=FALSE;
  
	/* 
	 * For every vcp, get the user and check his tokens 
	 */
	lock_ObtainWrite(&smb_rctLock);
	for(vcp=smb_allVCsp; vcp; vcp=vcp->nextp) {
	        for(usersp=vcp->usersp; usersp; usersp=usersp->nextp) {
				if (usersp->unp) {
		           if ((userp=usersp->unp->userp)==0)
					   continue;
				} else
					continue;
			lock_ObtainMutex(&userp->mx);
			for(ucellp=userp->cellInfop; ucellp; ucellp=ucellp->nextp) {
			  if(ucellp->flags & CM_UCELLFLAG_RXKAD) {
			    if(ucellp->expirationTime < now) {
				  /* this guy's tokens have expired */
				  osi_Log3(afsd_logp, "cm_CheckTokens: Tokens for user:%s have expired expiration time:0x%x ucellp:%x", ucellp->userName, ucellp->expirationTime, ucellp);
				  if (ucellp->ticketp) {
				          free(ucellp->ticketp);
					  ucellp->ticketp = NULL;
				  }
				  ucellp->flags &= ~CM_UCELLFLAG_RXKAD;
				  ucellp->gen++;
				  bExpired=TRUE;
			    }
			  } 
			}
			lock_ReleaseMutex(&userp->mx);
			if(bExpired) {
			        bExpired=FALSE;
				cm_ResetACLCache(userp);
			}
		}
	}
	lock_ReleaseWrite(&smb_rctLock);
}
