/* --------------------------------------------------------------------------
 *
 * License
 *
 * The contents of this file are subject to the Jabber Open Source License
 * Version 1.0 (the "JOSL").  You may not copy or use this file, in either
 * source code or executable form, except in compliance with the JOSL. You
 * may obtain a copy of the JOSL at http://www.jabber.org/ or at
 * http://www.opensource.org/.  
 *
 * Software distributed under the JOSL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the JOSL
 * for the specific language governing rights and limitations under the
 * JOSL.
 *
 * Copyrights
 * 
 * Portions created by or assigned to Jabber.com, Inc. are 
 * Copyright (c) 1999-2002 Jabber.com, Inc.  All Rights Reserved.  Contact
 * information for Jabber.com, Inc. is available at http://www.jabber.com/.
 *
 * Portions Copyright (c) 1998-1999 Jeremie Miller.
 * 
 * Acknowledgements
 * 
 * Special thanks to the Jabber Open Source Contributors for their
 * suggestions and support of Jabber.
 * 
 * Alternatively, the contents of this file may be used under the terms of the
 * GNU General Public License Version 2 or later (the "GPL"), in which case
 * the provisions of the GPL are applicable instead of those above.  If you
 * wish to allow use of your version of this file only under the terms of the
 * GPL and not to allow others to use your version of this file under the JOSL,
 * indicate your decision by deleting the provisions above and replace them
 * with the notice and other provisions required by the GPL.  If you do not
 * delete the provisions above, a recipient may use your version of this file
 * under either the JOSL or the GPL. 
 * 
 * 
 * users.c -- functions for manipulating data for logged in users
 * 
 --------------------------------------------------------------------------*/

#include "jsm.h"

int js__usercount = 0;
/*
 *  _js_users_del -- call-back for deleting user from the hash table
 *  
 *  This function is called periodically by the user data garbage collection
 *  thread. It removes users aren't logged in from the global hashtable.
 *
 *  parameters
 *  	arg -- not used
 *		key -- the users key in the hashtable, not used
 *      data -- the user data to check
 *
 *  returns
 *      1  
 */
int _js_users_del(void *arg, const void *key, void *data)
{
    HASHTABLE ht = (HASHTABLE)arg;
    udata u = (udata)data;	/* cast the pointer into udata */

    /*
     * if the reference count for this user's record
     * is positive, or if there are active sessions
     * we can't free it, so return immediately
     */
    if(u->ref > 0 || (u->sessions != NULL && ++js__usercount))
        return 1;

    log_debug(ZONE,"freeing %s",u->user);

    ghash_remove(ht,u->user);
    pool_free(u->p);

    return 1;
}


/* callback for walking the host hash tree */
int _js_hosts_del(void *arg, const void *key, void *data)
{
    HASHTABLE ht = (HASHTABLE)data;

    log_debug(ZONE,"checking users for host %s",(char*)key);

    ghash_walk(ht,_js_users_del,ht);

    return 1;
}

/*
 *  js_users_gc is a heartbeat that
 *  flushes old users from memory.  
 */
result js_users_gc(void *arg)
{
    jsmi si = (jsmi)arg;

    /* free user struct if we can */
    js__usercount = 0;
    ghash_walk(si->hosts,_js_hosts_del,NULL);
    log_debug("usercount","%d\ttotal users",js__usercount);
    return r_DONE;
}



/*
 *  js_user -- gets the udata record for a user
 *  
 *  js_user attempts to locate the user data record
 *  for the specifed id. First it looks in current list,
 *  if that fails, it looks in xdb and creates new list entry.
 *  If THAT fails, it returns NULL (not a user).
 */
udata js_user(jsmi si, jid id, HASHTABLE ht)
{
    pool p;
    udata cur, newu;
    char *ustr;
    xmlnode x, y;
    jid uid;

    if(si == NULL || id == NULL || id->user == NULL) return NULL;

    /* get the host hash table if it wasn't provided */
    if(ht == NULL)
        ht = ghash_get(si->hosts,id->server);

    /* hrm, like, this isn't our user! */
    if(ht == NULL) return NULL;

    /* copy the id and convert user to lower case */
    uid = jid_new(id->p, jid_full(jid_user(id)));
    for(ustr = uid->user; *ustr != '\0'; ustr++)
        *ustr = tolower(*ustr);

    /* debug message */
    log_debug(ZONE,"js_user(%s,%X)",jid_full(uid),ht);

    /* try to get the user data from the hash table */
    if((cur = ghash_get(ht,uid->user)) != NULL)
        return cur;

    /* debug message */
    log_debug(ZONE,"## js_user not current ##");

    /* try to get the user auth data from xdb */
    x = xdb_get(si->xc, uid, NS_AUTH);

    /* try to get hashed user auth data from xdb */
    y = xdb_get(si->xc, uid, NS_AUTH_CRYPT);

    /* does the user exist? */
    if (x == NULL && y == NULL)
	return NULL;

    /* create a udata struct */
    p = pool_heap(64);
    newu = pmalloco(p, sizeof(_udata));
    newu->p = p;
    newu->si = si;
    newu->user = pstrdup(p, uid->user);
    newu->pass = x ? pstrdup(p, xmlnode_get_data(x)) : NULL;
    newu->id = jid_new(p,jid_full(uid));
    if (x)
	xmlnode_free(x);
    if (y)
	xmlnode_free(y);


    /* got the user, add it to the user list */
    ghash_put(ht,newu->user,newu);
    log_debug(ZONE,"js_user debug %X %X",ghash_get(ht,newu->user),newu);

    return newu;
}

