root/usr/src/lib/print/libpapi-dynamic/common/nss.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
 *
 */

/* Id: nss.c 180 2006-07-20 17:33:02Z njacobs $ */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <syslog.h>
#include <papi.h>
#include <uri.h>
#include <papi_impl.h>
#ifdef NSS_EMULATION
#include <nss-emulation.h>
#elif NSS_SOLARIS
#include <nss_dbdefs.h>
#endif
#include <config-site.h>
#if defined(__sun) && defined(__SVR4)
#include <sys/systeminfo.h>
#endif


static char *
bsdaddr_to_uri(papi_attribute_t **list, char *bsdaddr)
{
        char *result = NULL;

        if (bsdaddr != NULL) {
                char *bsd[3], *tmp, *iter = NULL;
                char buf[512];

                tmp = strdup(bsdaddr);

                bsd[0] = strtok_r(tmp, ":,", &iter);
                if ((bsd[1] = strtok_r(NULL, ":,", &iter)) == NULL)
                        papiAttributeListGetString(list, NULL,
                            "printer-name", &bsd[1]);
                bsd[2] = strtok_r(NULL, ":,", &iter);

                snprintf(buf, sizeof (buf), "lpd://%s/printers/%s%s%s", bsd[0],
                    (bsd[1] != NULL) ? bsd[1] : "",
                    (bsd[2] != NULL) ? "#" : "",
                    (bsd[2] != NULL) ? bsd[2] : "");

                free(tmp);

                result = strdup(buf);
        }

        return (result);
}

#if defined(__sun) && defined(__SVR4)
/*
 * This is an awful HACK to force the dynamic PAPI library to use the
 * lpsched support when the destination apears to be a local lpsched
 * queue on Solaris.
 */
static void
solaris_lpsched_shortcircuit_hack(papi_attribute_t ***list)
{
        papi_attribute_t *attribute;
        uri_t *uri = NULL;
        char *printer = NULL;
        char buf[128], buf2[128];

        /* setting this in the calling env can be useful for debugging */
        if (getenv("DISABLE_LPSCHED_SHORTCIRCUIT") != NULL)
                return;

        papiAttributeListGetString(*list, NULL,
            "printer-uri-supported", &printer);
        /* if there is no printer-uri-supported, there is nothing to do */
        if (printer == NULL) {
                return;
        }

        if (uri_from_string(printer, &uri) < 0) {
                papiAttributeListFree(*list);
                *list = NULL;
                uri_free(uri);
                return;
        }

        /* already an lpsched URI ? */
        if (strcasecmp(uri->scheme, "lpsched") == 0) {
                uri_free(uri);
                return;
        }

        if (uri->path == NULL) {
                printer = "";
        } else {
                if ((printer = strrchr(uri->path, '/')) == NULL)
                        printer = uri->path;
                else
                        printer++;
        }

        /* is there an lpsched queue (printer/class) */
        snprintf(buf, sizeof (buf), "/etc/lp/interfaces/%s", printer);
        snprintf(buf2, sizeof (buf2), "/etc/lp/classes/%s", printer);
        if ((access(buf, F_OK) < 0) && (access(buf2, F_OK) < 0)) {
                uri_free(uri);
                return;
        }

        /* is this the "local" host */
        if ((uri->host != NULL) && (is_localhost(uri->host) == 0)) {
                uri_free(uri);
                return;
        }

        snprintf(buf, sizeof (buf), "lpsched://%s/printers/%s",
            (uri->host ? uri->host : "localhost"), printer);
        papiAttributeListAddString(list, PAPI_ATTR_REPLACE,
            "printer-uri-supported", buf);
        uri_free(uri);
}
#endif

static void
fill_printer_uri_supported(papi_attribute_t ***list)
{
        papi_attribute_t *attribute;
        char *string = NULL;

        /* do we have a printer-uri-supported */
        attribute = papiAttributeListFind(*list, "printer-uri-supported");
        if (attribute != NULL) /* we have what we need, return */
                return;

        /* do we have a printer-uri (in URI form) to rename */
        attribute = papiAttributeListFind(*list, "printer-uri");
        if ((attribute != NULL) &&
            (attribute->type == PAPI_STRING) &&
            (attribute->values != NULL) &&
            (attribute->values[0]->string != NULL) &&
            (strstr(attribute->values[0]->string, "://") != NULL)) {
                        /* rename it in place and return */
                free(attribute->name);
                attribute->name = strdup("printer-uri-supported");
                return;
        }

        /* do we have a printers.conf(5) "bsdaddr" to convert */
        papiAttributeListGetString(*list, NULL, "bsdaddr", &string);
        if (string != NULL) { /* parse it, convert it, add it */
                char *uri = bsdaddr_to_uri(*list, string);

                if (uri != NULL) {
                        papiAttributeListAddString(list, PAPI_ATTR_APPEND,
                            "printer-uri-supported", uri);
                        papiAttributeListDelete(list, "bsdaddr");
                        free(uri);
                        return;
                }
        }

        /* do we have a printers.conf(5) "rm" (and "rp") to convert */
        papiAttributeListGetString(*list, NULL, "rm", &string);
        if (string != NULL) {
                char *rp = NULL;

                /* default to "printer-name", but use "rp" if we have it */
                papiAttributeListGetString(*list, NULL, "printer-name", &rp);
                papiAttributeListGetString(*list, NULL, "rp", &rp);

                if (rp != NULL) { /* fill in the uri if we have the data */
                        char buf[BUFSIZ];

                        snprintf(buf, sizeof (buf), "lpd://%s/printers/%s",
                            string, rp);
                        papiAttributeListAddString(list, PAPI_ATTR_APPEND,
                            "printer-uri-supported", strdup(buf));
                        return;
                }
        }

        /* if were are here, we don't have a printer-uri-supported */
}

#ifdef NEED_BROKEN_PRINTER_URI_SEMANTIC
static void
fill_printer_uri(papi_attribute_t ***list)
{
        papi_attribute_t *attribute;
        char *uri = NULL;

        if ((list == NULL) || (*list == NULL))
                return;

        /* do we have a printer-uri */
        attribute = papiAttributeListFind(*list, "printer-uri");
        if (attribute != NULL) /* we have what we need, return */
                return;

        /*
         * this is sufficient to fool libgnomeprintpapi, but not promote it's
         * use in the future.
         */
        papiAttributeListAddString(list, PAPI_ATTR_EXCL, "printer-uri",
            "broken printer-uri semantic");
}
#endif /* NEED_BROKEN_PRINTER_URI_SEMANTIC */

static void
cvt_all_to_member_names(papi_attribute_t ***list)
{
        papi_status_t status;
        void *iter = NULL;
        char *string = NULL;

        papiAttributeListGetString(*list, NULL, "member-names", &string);
        if (string != NULL) /* already have a member-names */
                return;

        for (status = papiAttributeListGetString(*list, &iter, "all", &string);
            status == PAPI_OK;
            status = papiAttributeListGetString(*list, &iter, NULL, &string)) {
                char *s_iter = NULL, *value, *tmp = strdup(string);

                for (value = strtok_r(tmp, ", \t", &s_iter);
                    value != NULL;
                    value = strtok_r(NULL, ", \t", &s_iter))
                        papiAttributeListAddString(list, PAPI_ATTR_APPEND,
                            "member-names", value);
                free(tmp);
        }
}

static papi_attribute_t **
_cvt_nss_entry_to_printer(char *entry)
{
        char    *key = NULL;
        char    *cp;
        char    buf[BUFSIZ];
        int in_namelist = 1, buf_pos = 0;
        papi_attribute_t **list = NULL;

        if (entry == NULL)
                return (NULL);

        memset(buf, 0, sizeof (buf));
        for (cp = entry; *cp != '\0'; cp++) {
                switch (*cp) {
                case ':':       /* end of kvp */
                        if (in_namelist != 0) {
                                papiAttributeListAddString(&list,
                                    PAPI_ATTR_APPEND, "printer-name", buf);
                                in_namelist = 0;
                        } else if (key != NULL) {
                                papiAttributeListAddString(&list,
                                    PAPI_ATTR_APPEND, key, buf);
                                free(key);
                        }
                        memset(buf, 0, sizeof (buf));
                        buf_pos = 0;
                        key = NULL;
                        break;
                case '=':       /* kvp seperator */
                        if (key == NULL) {
                                key = strdup(buf);
                                memset(buf, 0, sizeof (buf));
                                buf_pos = 0;
                        } else
                                buf[buf_pos++] = *cp;
                        break;
                case '|':       /* namelist seperator */
                        if (in_namelist != 0) {
                                papiAttributeListAddString(&list,
                                    PAPI_ATTR_APPEND, "printer-name", buf);
                                memset(buf, 0, sizeof (buf));
                                buf_pos = 0;
                        } else  /* add it to the buffer */
                                buf[buf_pos++] = *cp;
                        break;
                case '\\':      /* escape char */
                        buf[buf_pos++] = *(++cp);
                        break;
                default:
                        buf[buf_pos++] = *cp;
                }

        }

        if (key != NULL) {
                papiAttributeListAddString(&list, PAPI_ATTR_APPEND, key, buf);
                free(key);
        }

        /* resolve any "use" references in the configuration DB */
        key = NULL;
        papiAttributeListGetString(list, NULL, "use", &key);
        if (key != NULL) {
                papi_attribute_t **use_attrs = getprinterbyname(key, NULL);

                list_concatenate(&list, use_attrs);
        }

        fill_printer_uri_supported(&list);
        cvt_all_to_member_names(&list); /* convert "all" to "member-names" */

        return (list);
}

#if defined(NSS_SOLARIS) && !defined(NSS_EMULATION)

#ifndef NSS_DBNAM__PRINTERS     /* not in nss_dbdefs.h because it's private */
#define NSS_DBNAM__PRINTERS     "_printers"
#endif

static DEFINE_NSS_DB_ROOT(db_root);
static DEFINE_NSS_GETENT(context);

static char *private_ns = NULL;

static void
_nss_initf_printers(p)
    nss_db_params_t *p;
{
        if (private_ns != NULL) {
                /*
                 * because we need to support a legacy interface that allows
                 * us to select a specific name service, we need to dummy up
                 * the parameters to use a private nsswitch database and set
                 * the * default_config entry to the name service we are
                 * looking into.
                 */
                p->name = NSS_DBNAM__PRINTERS;          /* "_printers" */
                p->default_config = private_ns;
        } else {
                /* regular behaviour */
                p->name = NSS_DBNAM_PRINTERS;    /* "printers" */
                p->default_config = NSS_DEFCONF_PRINTERS;
        }
        syslog(LOG_DEBUG, "database: %s, default: %s",
                (p->name ? p->name : "NULL"),
                (p->default_config ? p->default_config : "NULL"));
}

/*
 * Return values: 0 = success, 1 = parse error, 2 = erange ...
 * The structure pointer passed in is a structure in the caller's space
 * wherein the field pointers would be set to areas in the buffer if
 * need be. instring and buffer should be separate areas.
 */
/* ARGSUSED */
static int
str2printer(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
{
        if (lenstr + 1 > buflen)
                return (NSS_STR_PARSE_ERANGE);

        /* skip entries that begin with '#' */
        if (instr[0] == '#')
                return (NSS_STR_PARSE_PARSE);

        /*
         * We copy the input string into the output buffer
         */
        (void) memcpy(buffer, instr, lenstr);
        buffer[lenstr] = '\0';

        return (NSS_STR_PARSE_SUCCESS);
}
#endif /* NSS_SOLARIS */

int
setprinterentry(int stayopen, char *ns)
{
#ifdef NSS_EMULATION
        emul_setprinterentry(stayopen);
#elif NSS_SOLARIS
        private_ns = ns;
        nss_setent(&db_root, _nss_initf_printers, &context);
        private_ns = NULL;
#endif
        return (0);
}


int
endprinterentry(int i)
{
#ifdef NSS_EMULATION
        emul_endprinterentry();
#elif NSS_SOLARIS
        nss_endent(&db_root, _nss_initf_printers, &context);
        nss_delete(&db_root);
        private_ns = NULL;
#endif
        return (0);
}

/* ARGSUSED2 */
papi_attribute_t **
getprinterentry(char *ns)
{
        papi_attribute_t **result = NULL;

#if defined(NSS_EMULATION) || defined(NSS_SOLARIS)
        char buf[10240];
        nss_status_t    res = NSS_NOTFOUND;

#ifdef NSS_EMULATION
        res = emul_getprinterentry_r(buf, sizeof (buf));
#elif NSS_SOLARIS
        nss_XbyY_args_t arg;

        private_ns = ns;
        buf[0] = '\0';
        NSS_XbyY_INIT(&arg, buf, buf, sizeof (buf), str2printer);
        res = nss_getent(&db_root, _nss_initf_printers, &context, &arg);
        (void) NSS_XbyY_FINI(&arg);
        private_ns = NULL;
#endif

        if (res != NSS_SUCCESS)
                buf[0] = '\0';

        result = _cvt_nss_entry_to_printer(buf);
#if defined(__sun) && defined(__SVR4)
        solaris_lpsched_shortcircuit_hack(&result);
#endif
#ifdef NEED_BROKEN_PRINTER_URI_SEMANTIC
        fill_printer_uri(&result);
#endif /* NEED_BROKEN_PRINTER_URI_SEMANTIC */
#endif

#ifdef DEBUG
        printf("getprinterentry(%s): 0x%8.8x\n", (ns ? ns : "NULL"), result);
        if (result != NULL) {
                char buf[4096];

                papiAttributeListToString(result, "\n\t", buf, sizeof (buf));
                printf("\t%s\n", buf);
        }
#endif /* DEBUG */

        return (result);
}


papi_attribute_t **
getprinterbyname(char *name, char *ns)
{
        papi_attribute_t **result = NULL;

        if (strstr(name, "://") != NULL) {      /* shortcut for URI form */
                papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
                    "printer-name", name);
                papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
                    "printer-uri-supported", name);
        } else if (strchr(name, ':') != NULL) { /* shortcut for POSIX form */
                char *uri = bsdaddr_to_uri(result, name);

                papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
                    "printer-name", name);
                if (uri != NULL) {
                        papiAttributeListAddString(&result, PAPI_ATTR_APPEND,
                            "printer-uri-supported", uri);
                        free(uri);
                }
        } else {                                /* anything else */
#if defined(NSS_EMULATION) || defined(NSS_SOLARIS)
                char buf[10240];
                nss_status_t    res = NSS_NOTFOUND;

#ifdef NSS_EMULATION
                res = emul_getprinterbyname_r(name, buf, sizeof (buf));
#elif NSS_SOLARIS
                nss_XbyY_args_t arg;

                private_ns = ns;
                NSS_XbyY_INIT(&arg, buf, buf, sizeof (buf), str2printer);
                arg.key.name = name;
                res = nss_search(&db_root, _nss_initf_printers,
                    NSS_DBOP_PRINTERS_BYNAME, &arg);
                (void) NSS_XbyY_FINI(&arg);
                private_ns = NULL;

                if (res != NSS_SUCCESS)
                        buf[0] = '\0';
#endif

                result = _cvt_nss_entry_to_printer(buf);
#endif
        }
#if defined(__sun) && defined(__SVR4)
        solaris_lpsched_shortcircuit_hack(&result);
#endif
#ifdef DEBUG
        printf("getprinterbyname(%s): %s = 0x%8.8x\n", (ns ? ns : "NULL"),
            name, result);
        if (result != NULL) {
                char buf[4096];

                papiAttributeListToString(result, "\n\t", buf, sizeof (buf));
                printf("\t%s\n", buf);
        }
#endif /* DEBUG */

        return (result);
}