/** BEGIN COPYRIGHT BLOCK
 * Copyright (C) 2001 Sun Microsystems, Inc.  Used by permission.
 * Copyright (C) 2005 Red Hat, Inc.
 * All rights reserved.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation version
 * 2.1 of the License.
 *                                                                                  
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *                                                                                  
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 * END COPYRIGHT BLOCK **/
/* 
 * distadm.c: Functions for distributed admin
 *            
 * All blame to Mike McCool
 */
#include <stdlib.h>
#ifdef XP_WIN32
#include <windows.h>
#endif
#if HAVE_UNISTD_H == 1
#include <unistd.h>
#endif

/* Form new nspr20 */
#include <nspr.h>
#include <plstr.h>
#include <private/pprio.h>

#include "libadminutil/distadm.h"
#include "libadminutil/resource.h"
#include "dbtadmutil.h"

static int admutil_initialized=0;
Resource *admutil_i18nResource = NULL;
char *admutil_acceptLang = "en";

#ifdef XP_WIN32
PR_IMPLEMENT(int)
NTPriv_system_pread(PRFileDesc* fd, char *buf, int BytesToRead)
{
    unsigned long BytesRead = 0;
    int result = 0;
    BOOLEAN TimeoutSet = FALSE;

    /* XXXMB - nspr20 should be able to do this; but right now it doesn't
     * return proper error info.
     * fix it later...
     */
    if(ReadFile((HANDLE)PR_FileDesc2NativeHandle(fd), 
		(LPVOID)buf, BytesToRead, &BytesRead, NULL) == FALSE) {
        if (GetLastError() == ERROR_BROKEN_PIPE) {
            return -1;
        } else {
            return -1;
        }
    }
    return (BytesRead ? BytesRead : -1);
}
#endif


/* Initialize NSPR for all the base functions we use */
PR_IMPLEMENT(int)
ADMUTIL_Init(void)
{
  char *lang = getenv("HTTP_ACCEPT_LANGUAGE");
  int errcode;

  if(!admutil_initialized)  {
    PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 8);
    admutil_initialized=1;
  }

  if (!admutil_i18nResource) {
    admutil_i18nResource = res_find_and_init_resource(PROPERTYDIR, NULL);
    if (lang) admutil_acceptLang = PL_strdup(lang);
  }

  /* TODO: what to do about errors.  Should this always be done?
   */
  ADM_InitializePermissions(&errcode);

  return 0;
}

#ifndef MALLOC
#define MALLOC PR_Malloc
#endif


/* for old nspr20 
#ifndef MALLOC
#define MALLOC PR_MALLOC
#endif
*/

#ifndef BIG_LINE
#define BIG_LINE 1024
#endif

static char *user = ADM_NOT_INITIALIZED;
static char *pass = ADM_NOT_INITIALIZED;
static char *auth = ADM_NOT_INITIALIZED;
static char *userDN = ADM_NOT_INITIALIZED;

/* The function to use when reading the stuff from the pipe */
#ifdef XP_WIN32
#define SYSTEM_PREAD system_pread
#else /* XP_UNIX */
#define SYSTEM_PREAD system_fread
#endif

PR_IMPLEMENT(int)
ADM_InitializePermissions(int *errcode)
{
  /*
    int _ai=ADM_Init();
    */
    char *t = getenv("PASSWORD_PIPE");
    PRInt32 osfd = 0;
    PRFileDesc *fd = NULL;
    char *buf;
    PRInt32 bufsize;
    PRInt32 numread = 0;
    PRInt32 totalread = 0;
    char *head, *tail;
    int retval = 0;
    int rpterrcode = 0; /* for rpt_err */
    char *errmsg = NULL, *errdetail = NULL;
    int needfree = 0;
    

    /* No error in this case, because it's expected to happen sometimes */
    if(!t)   {
        user = NULL;
        pass = NULL;
        auth = NULL;
        return retval;
    }

    osfd = atol(t);
    if (osfd == STDIN_FILENO) {
        fd = PR_STDIN;
    } else {
        fd = PR_ImportFile(osfd);
    }

    buf = (char *) PR_Malloc(BIG_LINE);
    bufsize = BIG_LINE;

    while(1)  {
#ifdef XP_WIN32
        numread = NTPriv_system_pread(fd, buf, bufsize);
#else
        numread = PR_Read(fd, buf, bufsize);
#endif
        totalread += numread;
        if(numread < 0)  {
            /* MLM XXX - ERROR CODE */
            rpterrcode = SYSTEM_ERROR;
            retval = -1;
	        if (admutil_i18nResource) {
              errmsg = (char*)res_getstring(admutil_i18nResource,
				        DBT_distadm_pipeErr,
				        admutil_acceptLang, NULL, 0, NULL);
              errdetail = (char*)res_getstring(admutil_i18nResource,
				        DBT_distadm_pipeErrDetail,
				        admutil_acceptLang, NULL, 0, NULL);
              needfree = 1;
	        }
	        else {
              errmsg = "Could not read from pipe";
              errdetail = "Could not read authentication information from pipe.";
	        }
            goto cleanup;
        }  else  
        if(numread == 0)  {
            break;
        }
        if(buf[numread - 1] == '\0')  {
            break;
        }
    }
    buf[totalread] = '\0';

    /* Parse the buffer */
    head = buf;
    tail = PL_strchr(head, '\n');
    *tail++ = '\0';
    if(!PL_strncmp(head, ADM_USER_STRING, PL_strlen(ADM_USER_STRING)))  {
        user = PL_strdup(&(head[PL_strlen(ADM_USER_STRING)]));
        if(!PL_strcmp(user, ADM_NO_VALUE_STRING))  {
            user = NULL;
        }
    }
    head = tail;
    tail = PL_strchr(head, '\n');
    *tail++ = '\0';
    if(!PL_strncmp(head, ADM_PASS_STRING, PL_strlen(ADM_PASS_STRING)))  {
        pass = PL_strdup(&(head[PL_strlen(ADM_PASS_STRING)]));
        if(!PL_strcmp(pass, ADM_NO_VALUE_STRING))  {
            pass = NULL;
        }
    }
    head = tail;
    tail = PL_strchr(head, '\n');
    *tail++ = '\0';
    if(!PL_strncmp(head, ADM_AUTH_STRING, PL_strlen(ADM_AUTH_STRING)))  {
        auth = PL_strdup(&(head[PL_strlen(ADM_AUTH_STRING)]));
        if(!PL_strcmp(auth, ADM_NO_VALUE_STRING))  {
            auth = NULL;
        }
    }

    head = tail;
    tail = PL_strchr(head, '\n');
    *tail++ = '\0';
    if(!PL_strncmp(head, ADM_USERDN_STRING, PL_strlen(ADM_USERDN_STRING)))  {
        userDN = PL_strdup(&(head[PL_strlen(ADM_USERDN_STRING)]));
        if(!PL_strcmp(userDN, ADM_NO_VALUE_STRING))  {
            userDN = NULL;
        }
    }

    /* Parse SIE password */
    head = tail;
    tail = PL_strchr(head, '\n');
    if(tail) {
        /* We're reading the pipe from AS 4.2 and higher */
        *tail++ = '\0';
        if (!PL_strncmp(head, ADM_SIEPWD_STRING, PL_strlen(ADM_SIEPWD_STRING))) {
            char *siepwd = &head[PL_strlen(ADM_SIEPWD_STRING)];

            if (PL_strcmp(siepwd, ADM_NO_VALUE_STRING) != 0) {
                admSetCachedSIEPWD(siepwd);
            }
        }
    }
cleanup:
    if (fd != PR_STDIN) {
        PR_Close(fd);
    }
    if (errmsg) {
        rpt_err(rpterrcode, errmsg, errdetail, NULL);
        if (needfree) {
            PL_strfree(errmsg);
            PL_strfree(errdetail);
        }
    }
    return retval;
}

PR_IMPLEMENT(int)
ADM_GetCurrentUsername(int *errcode, char **username)
{
    int err;

    if((user) && (!PL_strcmp(user, ADM_NOT_INITIALIZED)))  {
        if(ADM_InitializePermissions(&err))  {
            *errcode = err;
            return -1;
        }
    }
    *username = user;
    return 0;
}

PR_IMPLEMENT(int)
ADM_GetCurrentPassword(int *errcode, char **password)
{
    int err;

    if((pass) && (!PL_strcmp(pass, ADM_NOT_INITIALIZED)))  {
        if(ADM_InitializePermissions(&err))  {
            *errcode = err;
            return -1;
        }
    }
    *password = pass;
    return 0;
}

PR_IMPLEMENT(int)
ADM_GetAuthorizationString(int *errcode, char **authstr)
{
    int err;

    if((auth) && (!PL_strcmp(auth, ADM_NOT_INITIALIZED)))  {
        if(ADM_InitializePermissions(&err))  {
            *errcode = err;
            return -1;
        }
    }
    *authstr = auth;
    return 0;
}

PR_IMPLEMENT(int)
ADM_GetUserDNString(int *errcode, char **userdn)
{
    int err;

    if((userDN) && (!PL_strcmp(userDN, ADM_NOT_INITIALIZED)))  {
        if(ADM_InitializePermissions(&err))  {
            *errcode = err;
            return -1;
        }
    }
    *userdn = userDN;
    return 0;
}
