/*
 * Copyright (c) 2001-2002 International Business Machines Corp.
 * 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
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
 * 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.
 */

#include <afs/param.h>

#include "Internal.h"
#include "org_openafs_jafs_Token.h"

#include <sys/stat.h>
#include <sys/ioctl.h>
#include <afs/vice.h>
#include <afs/venus.h>
#include <afs/afs_args.h>
/*#include <afs/afs_osi.h>
  #include <afs/afs_usrops.h>*/
#include <pthread.h>

#ifdef DMALLOC
#include "dmalloc.h"
#endif

pthread_mutex_t jafs_init_lock;

extern pthread_mutex_t jafs_login_lock;

extern int readCacheParms(char *afsMountPoint, char *afsConfDir,
			  char *afsCacheDir, int *cacheBlocks,
			  int *cacheFiles, int *cacheStatEntries,
			  int *dCacheSize, int *vCacheSize, int *chunkSize,
			  int *closeSynch, int *debug, int *nDaemons,
			  int *cacheFlags, char *logFile);

/**
 * Be carefull with the memory management:
 *
 * - For every getNativeString call the corresponding free().
 * - For every Get<type>ArrayElements call the corresponding
 *   Release<type>ArrayElements
 * - For every malloc call the corresponding free.
 */

int
osi_audit(void)
{
    return 0;
}

JNIEXPORT void JNICALL
Java_org_openafs_jafs_Token_callDebugger(JNIEnv * env, jobject obj)
{
    fprintf(stderr, "callDebugger called\n");
    __asm__("int $0x3");
}

/**
 * Initialize the user space library.
 *
 * The user space client must be initialized prior to any
 * user space related methods, including: klog, unlog, relog,
 * and shutdown.
 *
 * env      the Java environment
 * cls      the current Java class
 *
 * throws AFSException
 */
JNIEXPORT void JNICALL
Java_org_openafs_jafs_Token_initUserSpace(JNIEnv * env, jclass cls)
{
    char afsMountPoint[100], afsConfDir[100], afsCacheDir[100], logFile[100];
    jfieldID fid;
    int pagval;

    /* Initialize each init parameter with its associated default value */
    int cacheBlocks = 100000;
    int cacheFiles = 12500;
    int cacheStatEntries = 8192;
    int dCacheSize = 11398;
    int vCacheSize = 128;
    int chunkSize = 0;
    int closeSynch = 0;
    int debug = 0;
    int nDaemons = 3;
    int cacheFlags = -1;

    /* Initialize each init parameter with its associated default value */
    strcpy(afsMountPoint, "/afs");
    strcpy(afsConfDir, "/usr/afswsp/etc");
    strcpy(afsCacheDir, "/usr/afswsp/cache");
    strcpy(logFile, "/usr/afswsp/log/libjafs.log");

    pthread_mutex_init(&jafs_init_lock, NULL);
    pthread_mutex_lock(&jafs_init_lock);

    readCacheParms(afsMountPoint, afsConfDir, afsCacheDir, &cacheBlocks,
		   &cacheFiles, &cacheStatEntries, &dCacheSize, &vCacheSize,
		   &chunkSize, &closeSynch, &debug, &nDaemons, &cacheFlags,
		   logFile);

    /* See cache.tune for configuration details */
    if (debug) {
	fprintf(stderr, "uafs_Init(\"init_native\", \"%s\", \"%s\", \"%s\",
                      %d, %d, %d,
                      %d, %d, %d,
                      %d, %d, %d, %d, \"%s\");\n", afsMountPoint, afsConfDir, afsCacheDir, cacheBlocks, cacheFiles, cacheStatEntries, dCacheSize, vCacheSize, chunkSize, closeSynch, debug, nDaemons, cacheFlags, logFile);
    }
    uafs_Init("init_native", afsMountPoint, afsConfDir, afsCacheDir,
	      cacheBlocks, cacheFiles, cacheStatEntries, dCacheSize,
	      vCacheSize, chunkSize, closeSynch, debug, nDaemons, cacheFlags,
	      logFile);


    /* make the initial pag the unauthenticated pag */
    afs_setpag();
    uafs_unlog();
    pagval = afs_getpag_val();

    fid = (*env)->GetStaticFieldID(env, cls, "ANYUSER_PAG_ID", "I");
    if (fid == 0) {
	fprintf(stderr,
		"UserToken::init(): GetFieldID (ANYUSER_PAG_ID) failed\n");
	return;
    }

    (*env)->SetStaticIntField(env, cls, fid, pagval);

    pthread_mutex_unlock(&jafs_init_lock);
}

/**
 * Authenticates a user in kas, and binds that authentication
 * to the current process.
 *
 * env      the Java environment
 * obj      the current Java class
 * loginUTF    the login to authenticate (expected as username@cellname)
 * passwordUTF the password of the login
 * id		the existing pag (or 0)
 *
 * returns the assigned pag
 *
 * throws AFSException
 */
JNIEXPORT jint JNICALL
Java_org_openafs_jafs_Token_klog(JNIEnv * env, jobject obj, jstring jusername,
				 jstring jpassword, jstring jcell, jint id)
{
    char *username;
    char *password;
    char *cell;
    char *reason;
    int code;
    jint rc = -1;

    if (jcell != NULL) {
	cell = getNativeString(env, jcell);
	if (!cell) {
	    fprintf(stderr, "UserToken::klog(): failed to get cell name\n");
	    throwMessageException(env,
				  "Internal error, failed to translate cell name.");
	    return -1;
	}
    } else {
	cell = NULL;
    }

    if (jusername != NULL) {
	username = getNativeString(env, jusername);
	if (!username) {
	    if (cell != NULL)
		free(cell);
	    fprintf(stderr, "UserToken::klog(): failed to get username\n");
	    throwMessageException(env,
				  "Internal error, failed to translate username.");
	    return -1;
	}
    } else {
	if (cell != NULL)
	    free(cell);
	throwAFSException(env, JAFSNULLUSER);
	return -1;
    }

    if (jpassword != NULL) {
	password = getNativeString(env, jpassword);
	if (!password) {
	    if (cell != NULL)
		free(cell);
	    free(username);
	    fprintf(stderr, "UserToken::klog(): failed to get password\n");
	    throwMessageException(env,
				  "Internal error, failed to translate password.");
	    return -1;
	}
    } else {
	if (cell != NULL)
	    free(cell);
	free(username);
	throwAFSException(env, JAFSNULLPASS);
	return -1;
    }

    if (id == 0) {
	code = uafs_klog(username, cell, password, &reason);
    } else {
	/* Use existing PAG for this thread */
	code = afs_setpag_val(id);
	if (code != 0)
	    code = 180492L;	/* KABADARGUMENT */
	if (!code)
	    code = uafs_klog_nopag(username, cell, password, &reason);
    }

    if (code != 0) {
	if (cell != NULL)
	    free(cell);
	if (username != NULL)
	    free(username);
	if (password != NULL)
	    free(password);
	fprintf(stderr,
		"UserToken::klog(): uafs_klog failed to cell %s: %s\n", cell,
		reason);
	fprintf(stderr, "code = %d\n", code);
	throwAFSException(env, code);
	return -1;
    }

    /* Get the PAG we were assigned as the return value */
    rc = afs_getpag_val();

    /* clean up */
    if (cell != NULL)
	free(cell);
    if (username != NULL)
	free(username);
    if (password != NULL)
	free(password);

    /* return PAG ID */
    return rc;
}

/**
 * Authenticates a user in KAS by a previously acquired PAG ID, and binds 
 * that authentication to the current thread or native process.
 *
 * <P> This method does not require the user's username and password to
 * fully authenticate their request.  Rather it utilizes the user's PAG ID
 * to recapture the user's existing credentials.
 *
 * env      the Java environment
 * obj      the current Java class
 * id       User's current PAG (process authentication group) ID
 *
 * throws AFSException
 */
JNIEXPORT void JNICALL
Java_org_openafs_jafs_Token_relog(JNIEnv * env, jobject obj, jint id)
{
    int rc;

    rc = afs_setpag_val(id);

    if (rc != 0) {
	throwAFSException(env, rc);
    }
}

/**
 * Authenticates a user in KAS, and binds that authentication
 * to the current process.
 *
 * env      the Java environment
 * obj      the current Java class
 *
 * throws AFSException
 */
JNIEXPORT void JNICALL
Java_org_openafs_jafs_Token_unlog(JNIEnv * env, jobject obj)
{
    int rc;

    rc = uafs_unlog();

    if (rc != 0) {
	throwAFSException(env, rc);
    }
}

/**
 * Inform the native library that the application is 
 * shutting down and will be unloading.
 *
 * <p> The library will make a call informing the file server that it will 
 * no longer be available for callbacks.
 *
 * env      the Java environment
 * obj      the current Java class
 *
 * throws AFSException
 */
JNIEXPORT void JNICALL
Java_org_openafs_jafs_Token_shutdown(JNIEnv * env, jobject obj)
{
    uafs_Shutdown();
}
