/* pggconfig.c - general GnuPG configuration
 *      Copyright (C) 1999 Michael Roth <mroth@gnupg.org>
 *
 * This file is part of PGG (Privacy Guard Glue).
 *
 * PGG is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * PGG 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */


#include <includes.h>
#include <pgg.h>
#include <pggdebug.h>
#include <pggconfig.h>


#define cfg		((PggConfigPtr)(_cfg))
#define old_cfg		((PggConfigPtr)(_old_cfg))






PggConfig pgg_config_new(PggErrenv errenv)
{
    PggConfigPtr	new_cfg;
    
    PGG_CHECK_SKIP_ARG(NULL);
    
    if (!( new_cfg = _malloc(PggConfig)))
        PGG_RETURN_ERR_ARG(RESOURCE, MEMORY, NULL);
    
    memset(new_cfg, 0, _size(PggConfig));
    
    new_cfg->magic      = PggConfigMAGIC;
    new_cfg->refcounter = 1;
    
    return _hd(PggConfig, new_cfg);
}


static void destroy(PggConfigPtr cfgptr)
{
    if (!cfgptr) {
        PGG_DEBUG(("destroy with null pointer"));
        return;
    }
    
    free(cfgptr->gpgpath);
    free(cfgptr->configfile);
    free(cfgptr->public_keyring);
    free(cfgptr->secret_keyring);
    free(cfgptr->trustdb);
    free(cfgptr);
}


PggConfig pgg_config_clone(PggConfig _old_cfg, PggErrenv errenv)
{
    PggConfigPtr	new_cfg;
    
    PGG_STD_ASSERT_ARG(PggConfig, old_cfg, NULL);
    
    if (!( new_cfg = _malloc(PggConfig)))
        PGG_RETURN_ERR_ARG(RESOURCE, MEMORY, NULL);
    
    memset(new_cfg, 0, _size(PggConfig));
    
    new_cfg->magic      = PggConfigMAGIC;
    new_cfg->refcounter = 1;
    
    #define DUPLICATE(entry) if (old_cfg->entry && !(new_cfg->entry = strdup(old_cfg->entry))) { \
    				 destroy(new_cfg); PGG_RETURN_ERR_ARG(RESOURCE, MEMORY, NULL); }
    
    DUPLICATE(gpgpath)
    DUPLICATE(configfile)
    DUPLICATE(public_keyring)
    DUPLICATE(secret_keyring)
    DUPLICATE(trustdb)
    
    #undef DUPLICATE
    
    return _hd(PggConfig, new_cfg);
}


void pgg_config_release(PggConfig _cfg, PggErrenv errenv)
{
    PGG_STD_ASSERT(PggConfig, cfg);
    
    if (!--cfg->refcounter)
        destroy(cfg);
}


void pgg_config_addref(PggConfig _cfg, PggErrenv errenv)
{
    PGG_STD_ASSERT(PggConfig, cfg);
    cfg->refcounter++;
}


void pgg_config_set_gpgpath(PggConfig _cfg, const char *gpgpath, PggErrenv errenv)
{
    char *		tmp = NULL;
    
    PGG_STD_ASSERT(PggConfig, cfg);
    
    if (gpgpath && !(tmp = strdup(gpgpath)) )
        PGG_RETURN_ERR(RESOURCE, MEMORY);
    
    free(cfg->gpgpath);
    
    cfg->gpgpath = tmp;
}


const char * pgg_config_get_gpgpath(PggConfig _cfg, PggErrenv errenv)
{
    PGG_STD_ASSERT_ARG(PggConfig, cfg, NULL);
    PGG_ASSERT_ARG(cfg->gpgpath, REQUEST, NOTSET, NULL);
    return cfg->gpgpath;
}


void pgg_config_set_configfile(PggConfig _cfg, const char * configfile, PggErrenv errenv)
{
    char *		tmp = NULL;
    
    PGG_STD_ASSERT(PggConfig, cfg);
    
    if (configfile && !(tmp = strdup(configfile)) )
        PGG_RETURN_ERR(RESOURCE, MEMORY);
    
    free(cfg->configfile);
    
    cfg->configfile = tmp;
}


const char * pgg_config_get_configfile(PggConfig _cfg, PggErrenv errenv)
{
    PGG_STD_ASSERT_ARG(PggConfig, cfg, NULL);
    PGG_ASSERT_ARG(cfg->configfile, REQUEST, NOTSET, NULL);
    return cfg->configfile; 
}


void pgg_config_set_public_keyring(PggConfig _cfg, const char * public_keyring, PggErrenv errenv)
{
    char *              tmp = NULL;
  
    PGG_STD_ASSERT(PggConfig, cfg);

    if (public_keyring && !(tmp = strdup(public_keyring)) )
        PGG_RETURN_ERR(RESOURCE, MEMORY);

    free(cfg->public_keyring);

    cfg->public_keyring = tmp;
}


void pgg_config_set_secret_keyring(PggConfig _cfg, const char * secret_keyring, PggErrenv errenv)
{
    char *              tmp = NULL;

    PGG_STD_ASSERT(PggConfig, cfg);

    if (secret_keyring && !(tmp = strdup(secret_keyring)) )
        PGG_RETURN_ERR(RESOURCE, MEMORY);

    free(cfg->secret_keyring);

    cfg->secret_keyring = tmp;
}


const char * pgg_config_get_public_keyring(PggConfig _cfg, PggErrenv errenv) 
{
    PGG_STD_ASSERT_ARG(PggConfig, cfg, NULL);
    PGG_ASSERT_ARG(cfg->public_keyring, REQUEST, NOTSET, NULL);
    return cfg->public_keyring; 
}    


const char * pgg_config_get_secret_keyring(PggConfig _cfg, PggErrenv errenv) 
{
    PGG_STD_ASSERT_ARG(PggConfig, cfg, NULL);
    PGG_ASSERT_ARG(cfg->secret_keyring, REQUEST, NOTSET, NULL);
    return cfg->secret_keyring; 
}    


void pgg_config_set_trustdb(PggConfig _cfg, const char * trustdb, PggErrenv errenv)
{
    char *              tmp = NULL;

    PGG_STD_ASSERT(PggConfig, cfg);

    if (trustdb && !(tmp = strdup(trustdb)) )
        PGG_RETURN_ERR(RESOURCE, MEMORY);

    free(cfg->trustdb);

    cfg->trustdb = tmp;
}


const char * pgg_config_get_trustdb(PggConfig _cfg, PggErrenv errenv) 
{
    PGG_STD_ASSERT_ARG(PggConfig, cfg, NULL);
    PGG_ASSERT_ARG(cfg->trustdb, REQUEST, NOTSET, NULL);
    return cfg->trustdb; 
}    


int pgg_config_search_gpg(PggConfig _cfg, const char * path, PggErrenv errenv)
{
    char		buffer[PATH_MAX];
    char *		tmp;
    char *		search_path;
    int			i;
    
    PGG_STD_ASSERT_ARG(PggConfig, cfg, 0);
    
    if (!path)
        if (!( path = getenv("PATH") ))
            path = "/bin:/usr/bin";			/* FIXME: make this sense? */
    
    if (!( search_path = strdup(path) ))
        PGG_RETURN_ERR_ARG(RESOURCE, MEMORY, 0);
    
    for (tmp = strtok(search_path, ":"); tmp; tmp = strtok(NULL, ":")) {
        PGG_DEBUG(("looking for gpg in directory: \"%s\"", tmp));
        if ( (i = strlen(tmp)) > 0 ) {
            i--;
            while (i>=0 && tmp[i]=='/')		/* Don't remove the slash from a "/" path */
                tmp[i--] = 0;
            
            PGG_DEBUG(("directory without trailing slashes: \"%s\"", tmp));
            
            if (*tmp != 0 && strlen(tmp)+4 < PATH_MAX) {	/* empty and to large directory pathes are not permitted */
                strcpy(buffer, tmp);
                strcat(buffer, "/gpg");
                
                PGG_DEBUG(("path to test: \"%s\"", buffer));
                
                if (access(buffer, X_OK|F_OK) == 0) {
                    /*
                     * We found an executable gpg binary.
                     */
                    
                    PGG_DEBUG(("found gpg in: %s", buffer));
                    
                    free(search_path);
                    
                    if (!( tmp = strdup(buffer) ))
                        PGG_RETURN_ERR_ARG(RESOURCE, MEMORY, 0);
                    
                    free(cfg->gpgpath);
                    cfg->gpgpath = tmp;
                    
                    return 1;		/* Yes, we found gpg */
                    
                } /* if (access(...)==0) */
            } /* if (strlen(tmp)+4 < PATH_MAX) */
        } /* if (strlen(tmp) > 0) */
    } /* for (strtok(...)) */
    
    free(search_path);
    
    PGG_RETURN_ERR_ARG(RESOURCE, NOTFOUND, 0);
}


void pgg_config_setup_exe(PggConfig _cfg, PggExe exe, PggErrenv errenv)
{
    PggErrenv		local_errenv;
    
    PGG_STD_ASSERT(PggConfig, cfg);
    PGG_ASSERT(exe, ARGUMENT, NULLPTR);
    
    pgg_errenv_reset(local_errenv);
    
    if (!cfg->gpgpath)
        pgg_config_search_gpg(_cfg, NULL, local_errenv);
    
    pgg_exe_set_gpgpath(exe, cfg->gpgpath, local_errenv);
    
    if (cfg->configfile) {
        pgg_exe_add_arg(exe, "--config", local_errenv);
        pgg_exe_add_arg(exe, cfg->configfile, local_errenv);
    }
    
    if (cfg->trustdb) {
        pgg_exe_add_arg(exe, "--trustdb-name", local_errenv);
        pgg_exe_add_arg(exe, cfg->trustdb, local_errenv);
    }
    
    if (cfg->public_keyring || cfg->secret_keyring)
        pgg_exe_add_arg(exe, "--no-default-keyring", local_errenv);
    
    if (cfg->public_keyring) {
        pgg_exe_add_arg(exe, "--keyring", local_errenv);
        pgg_exe_add_arg(exe, cfg->public_keyring, local_errenv);
    }
    
    if (cfg->secret_keyring) {
        pgg_exe_add_arg(exe, "--secret-keyring", local_errenv);
        pgg_exe_add_arg(exe, cfg->public_keyring, local_errenv);
    }
    
    if (pgg_errenv_is_set(local_errenv))
        pgg_errenv_copy(errenv, local_errenv);
}




