root/usr/src/lib/print/libpapi-ipp/common/job.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 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 *
 */

/* $Id: job.c 148 2006-04-25 16:54:17Z njacobs $ */


/*LINTLIBRARY*/

#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <papi_impl.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <libintl.h>

#ifndef OPID_CUPS_MOVE_JOB
#define OPID_CUPS_MOVE_JOB 0x400D
#endif

void
papiJobFree(papi_job_t job)
{
        job_t *tmp = (job_t *)job;

        if (tmp != NULL) {
                if (tmp->attributes != NULL)
                        papiAttributeListFree(tmp->attributes);
                free(tmp);
        }
}

void
papiJobListFree(papi_job_t *jobs)
{
        if (jobs != NULL) {
                int i;

                for (i = 0; jobs[i] != NULL; i++)
                        papiJobFree(jobs[i]);
                free(jobs);
        }
}

papi_attribute_t **
papiJobGetAttributeList(papi_job_t job)
{
        papi_attribute_t **result = NULL;
        job_t *j = job;

        if (j != NULL)
                result = j->attributes;

        return (result);
}

char *
papiJobGetPrinterName(papi_job_t job)
{
        char *result = NULL;
        job_t *j = job;

        if (j != NULL)
                (void) papiAttributeListGetString(j->attributes, NULL,
                    "printer-name", &result);

        return (result);
}

int32_t
papiJobGetId(papi_job_t job)
{
        int32_t result = -1;
        job_t *j = job;

        if (j != NULL)
                (void) papiAttributeListGetInteger(j->attributes, NULL,
                    "job-id", &result);

        return (result);
}

papi_job_ticket_t *
papiJobGetJobTicket(papi_job_t job)
{
        papi_job_ticket_t *result = NULL;

        return (result);
}

static void
populate_job_request(service_t *svc, papi_attribute_t ***request,
                papi_attribute_t **attributes, char *printer, uint16_t type)
{
        papi_attribute_t **operational = NULL, **job = NULL;
        static char *operational_names[] = {
                "job-name", "ipp-attribute-fidelity", "document-name",
                "compression", "document-format", "document-natural-language",
                "job-k-octets", "job-impressions", "job-media-sheets", NULL
        };

        /* create the base IPP request */
        ipp_initialize_request(svc, request, type);

        /* create an operational attributes group */
        ipp_initialize_operational_attributes(svc, &operational, printer, -1);

        /* split up the attributes into operational and job attributes */
        split_and_copy_attributes(operational_names, attributes,
            &operational, &job);

        /* add the operational attributes group to the request */
        papiAttributeListAddCollection(request, PAPI_ATTR_REPLACE,
            "operational-attributes-group", operational);
        papiAttributeListFree(operational);

        /* add the job attributes group to the request */
        if (job != NULL) {
                /*
                 * Add job-originating-host-name to attributes
                 * if not already set.
                 */
                char *hostname = NULL;

                papiAttributeListGetString(job, NULL,
                    "job-originating-host-name", &hostname);

                if (hostname == NULL) {
                        char host[BUFSIZ];

                        if (gethostname(host, sizeof (host)) == 0)
                                papiAttributeListAddString(&job, PAPI_ATTR_EXCL,
                                    "job-originating-host-name", host);
                }

                papiAttributeListAddCollection(request, PAPI_ATTR_REPLACE,
                    "job-attributes-group", job);
                papiAttributeListFree(job);
        }
}

static papi_status_t
send_document_uri(service_t *svc, char *file, papi_attribute_t **attributes,
                char *printer, int32_t id, char last, uint16_t type)
{
        papi_status_t result = PAPI_INTERNAL_ERROR;
        papi_attribute_t **request = NULL, **op = NULL, **response = NULL;

        /* create the base IPP request */
        ipp_initialize_request(svc, &request, type);

        /* create an operational attributes group */
        ipp_initialize_operational_attributes(svc, &op, printer, id);

        papiAttributeListAddString(&op, PAPI_ATTR_REPLACE, "document-name",
            file);
        papiAttributeListAddBoolean(&op, PAPI_ATTR_REPLACE, "last-document",
            (last ? PAPI_TRUE : PAPI_FALSE));
        papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
            "operational-attributes-group", op);
        papiAttributeListFree(op);

        /* send the IPP request to the server */
        result = ipp_send_request_with_file(svc, request, &response, file);
        papiAttributeListFree(request);
        papiAttributeListFree(response);

        return (result);
}

typedef enum {_WITH_DATA, _BY_REFERENCE, _VALIDATE} call_type_t;

papi_status_t
internal_job_submit(papi_service_t handle, char *printer,
                papi_attribute_t **job_attributes,
                papi_job_ticket_t *job_ticket,
                char **files, papi_job_t *job,
                call_type_t call_type)
{
        papi_status_t result = PAPI_INTERNAL_ERROR;
        service_t *svc = handle;
        struct stat statbuf;
        job_t *j = NULL;
        int i;
        uint16_t req_type = OPID_PRINT_JOB;
        uint16_t data_type = OPID_SEND_DOCUMENT;
        papi_attribute_t **request = NULL, **response = NULL;

        if ((svc == NULL) || (printer == NULL) || (job == NULL))
                return (PAPI_BAD_ARGUMENT);

        switch (call_type) {
        case _BY_REFERENCE:
#ifdef SOME_DAY_WE_WILL_BE_ABLE_TO_USE_URIS_FOR_JOB_DATA
                /*
                 * For the time being, this is disabled.  There are a number
                 * of issues to be dealt with before we can send a URI
                 * across the network to the server.  For example, the file
                 * name(s) passed in are most likely relative to the current
                 * hosts filesystem.  They also most likely will require some
                 * form of authentication information to be passed with the
                 * URI.
                 */
                req_type = OPID_PRINT_URI;
                req_type = OPID_SEND_URI;
#endif
                /* fall-through */
        case _WITH_DATA:
                if ((files == NULL) || (files[0] == NULL))
                        return (PAPI_BAD_ARGUMENT);

                if (files[1] != NULL)   /* more than 1 file */
                        req_type = OPID_CREATE_JOB;

                break;
        case _VALIDATE:
                req_type = OPID_VALIDATE_JOB;
                /* if we have files, validate access to them */
                if (files != NULL) {
                        for (i = 0; files[i] != NULL; i++) {
                                if (access(files[i], R_OK) < 0) {
                                        detailed_error(svc, "%s: %s", files[i],
                                            strerror(errno));
                                        return (PAPI_DOCUMENT_ACCESS_ERROR);
                                }

                                if (strcmp("standard input", files[i]) != 0) {
                                        if (stat(files[i], &statbuf) < 0) {
                                                detailed_error(svc, gettext(
                                                    "Cannot access file: %s:"
                                                    " %s"), files[i],
                                                    strerror(errno));
                                                return (
                                                    PAPI_DOCUMENT_ACCESS_ERROR);
                                        }
                                        if (statbuf.st_size == 0) {
                                                detailed_error(svc,
                                                    "Zero byte (empty) file: "
                                                    "%s",
                                                    files[i]);
                                                return (PAPI_BAD_ARGUMENT);
                                        }
                                }
                        }
                        files = NULL;
                }
                break;
        }

        /* if we are already connected, use that connection. */
        if (svc->connection == NULL)
                if ((result = service_connect(svc, printer)) != PAPI_OK)
                        return (result);

        if ((*job = j = calloc(1, sizeof (*j))) == NULL)
                return (PAPI_TEMPORARY_ERROR);

        /*
         * before creating IPP request
         * add the job-name
         */
        if ((files != NULL) && (files[0] != NULL))
                papiAttributeListAddString(&job_attributes, PAPI_ATTR_EXCL,
                    "job-name", files[0]);

        /* create IPP request */
        populate_job_request(svc, &request, job_attributes, printer, req_type);

        switch (req_type) {
        case OPID_PRINT_JOB:
                result = ipp_send_request_with_file(svc, request, &response,
                    files[0]);
                break;
        case OPID_CREATE_JOB:
        case OPID_VALIDATE_JOB:
        case OPID_PRINT_URI:
                result = ipp_send_request(svc, request, &response);
                break;
        }
        papiAttributeListFree(request);

        if (result == PAPI_OK) {
                papi_attribute_t **op = NULL;

                /* retrieve the job attributes */
                papiAttributeListGetCollection(response, NULL,
                    "job-attributes-group", &op);
                copy_attributes(&j->attributes, op);

                if (req_type == OPID_CREATE_JOB) {
                        int32_t id = 0;

                        papiAttributeListGetInteger(j->attributes, NULL,
                            "job-id", &id);
                        /* send each document */
                        for (i = 0; ((result == PAPI_OK) && (files[i] != NULL));
                            i++)
                                result = send_document_uri(svc, files[i],
                                    job_attributes,
                                    printer, id, (files[i+1]?0:1),
                                    data_type);
                }
        }
        papiAttributeListFree(response);

        return (result);
}

papi_status_t
papiJobSubmit(papi_service_t handle, char *printer,
                papi_attribute_t **job_attributes,
                papi_job_ticket_t *job_ticket, char **files, papi_job_t *job)
{
        return (internal_job_submit(handle, printer, job_attributes,
            job_ticket, files, job, _WITH_DATA));
}

papi_status_t
papiJobSubmitByReference(papi_service_t handle, char *printer,
                papi_attribute_t **job_attributes,
                papi_job_ticket_t *job_ticket, char **files, papi_job_t *job)
{
        return (internal_job_submit(handle, printer, job_attributes,
            job_ticket, files, job, _BY_REFERENCE));
}

papi_status_t
papiJobValidate(papi_service_t handle, char *printer,
                papi_attribute_t **job_attributes,
                papi_job_ticket_t *job_ticket, char **files, papi_job_t *job)
{
        return (internal_job_submit(handle, printer, job_attributes,
            job_ticket, files, job, _VALIDATE));
}

papi_status_t
papiJobStreamOpen(papi_service_t handle, char *printer,
                papi_attribute_t **job_attributes,
                papi_job_ticket_t *job_ticket, papi_stream_t *stream)
{
        papi_status_t result = PAPI_INTERNAL_ERROR;
        papi_attribute_t **request = NULL;
        service_t *svc = handle;

        if ((svc == NULL) || (printer == NULL) || (stream == NULL))
                return (PAPI_BAD_ARGUMENT);

        /* if we are already connected, use that connection. */
        if (svc->connection == NULL)
                if ((result = service_connect(svc, printer)) != PAPI_OK)
                        return (result);

        papiAttributeListAddString(&job_attributes, PAPI_ATTR_EXCL,
            "job-name", "standard input");

        /* create job request */
        populate_job_request(svc, &request, job_attributes, printer,
            OPID_PRINT_JOB);

        *stream = svc->connection;

        result = ipp_send_initial_request_block(svc, request, 0);
        papiAttributeListFree(request);

        return (result);
}

papi_status_t
papiJobStreamWrite(papi_service_t handle,
                papi_stream_t stream, void *buffer, size_t buflen)
{
        papi_status_t result = PAPI_OK;
        service_t *svc = handle;
        size_t rc;

#ifdef DEBUG
        printf("papiJobStreamWrite(0x%8.8x, 0x%8.8x, 0x%8.8x, %d)\n",
            handle, stream, buffer, buflen);
        httpDumpData(stdout, "papiJobStreamWrite:", buffer, buflen);
#endif

        if ((svc == NULL) || (stream == NULL) || (buffer == NULL) ||
            (buflen == 0))
                return (PAPI_BAD_ARGUMENT);

        while ((result == PAPI_OK) && (buflen > 0)) {
                rc = ipp_request_write(svc, buffer, buflen);
                if (rc < 0)
                        result = PAPI_TEMPORARY_ERROR;
                else {
                        buffer = (char *)buffer + rc;
                        buflen -= rc;
                }
        }

#ifdef DEBUG
        printf("papiJobStreamWrite(): %s\n", papiStatusString(result));
#endif

        return (result);
}

papi_status_t
papiJobStreamClose(papi_service_t handle,
                papi_stream_t stream, papi_job_t *job)
{
        papi_status_t result = PAPI_INTERNAL_ERROR;
        http_status_t status = HTTP_CONTINUE;
        service_t *svc = handle;
        papi_attribute_t **response = NULL;
        job_t *j = NULL;

        if ((svc == NULL) || (stream == NULL) || (job == NULL))
                return (PAPI_BAD_ARGUMENT);

        if ((*job = j = calloc(1, sizeof (*j))) == NULL)
                return (PAPI_TEMPORARY_ERROR);

        (void) ipp_request_write(svc, "", 0);

        /* update our connection info */
        while (status == HTTP_CONTINUE)
                status = httpUpdate(svc->connection);

        if (status != HTTP_OK)
                return (http_to_papi_status(status));
        httpWait(svc->connection, 1000);

        /* read the IPP response */
        result = ipp_read_message(&ipp_request_read, svc, &response,
            IPP_TYPE_RESPONSE);
        if (result == PAPI_OK)
                result = ipp_status_info(svc, response);

        if (result == PAPI_OK) {
                papi_attribute_t **op = NULL;

                papiAttributeListGetCollection(response, NULL,
                    "job-attributes-group", &op);
                copy_attributes(&j->attributes, op);
        }
        papiAttributeListFree(response);

        return (result);
}

papi_status_t
papiJobQuery(papi_service_t handle, char *printer, int32_t job_id,
                char **requested_attrs,
                papi_job_t *job)
{
        papi_status_t result = PAPI_INTERNAL_ERROR;
        service_t *svc = handle;
        job_t *j = NULL;
        papi_attribute_t **request = NULL, **op = NULL, **response = NULL;

        if ((svc == NULL) || (printer == NULL))
                return (PAPI_BAD_ARGUMENT);

        /* if we are already connected, use that connection. */
        if (svc->connection == NULL)
                if ((result = service_connect(svc, printer)) != PAPI_OK)
                        return (result);

        if ((*job = j = calloc(1, sizeof (*j))) == NULL)
                return (PAPI_TEMPORARY_ERROR);

        ipp_initialize_request(svc, &request, OPID_GET_JOB_ATTRIBUTES);

        ipp_initialize_operational_attributes(svc, &op, printer, job_id);

        if (requested_attrs != NULL) {
                int i;

                for (i = 0; requested_attrs[i] != NULL; i++)
                        papiAttributeListAddString(&op, PAPI_ATTR_APPEND,
                            "requested-attributes", requested_attrs[i]);
        }

        papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
            "operational-attributes-group", op);
        papiAttributeListFree(op);
        result = ipp_send_request(svc, request, &response);
        papiAttributeListFree(request);

        op = NULL;
        papiAttributeListGetCollection(response, NULL,
            "job-attributes-group", &op);
        copy_attributes(&j->attributes, op);
        papiAttributeListFree(response);

        return (result);
}

/* papiJob{Cancel|Hold|Release|Restart|Promote} are all the same */
static papi_status_t
_job_cancel_hold_release_restart_promote(papi_service_t handle,
                char *printer, int32_t job_id, uint16_t type)
{
        papi_status_t result = PAPI_INTERNAL_ERROR;
        service_t *svc = handle;
        papi_attribute_t **request = NULL, **op = NULL, **response = NULL;

        if ((svc == NULL) || (printer == NULL) || (job_id < 0))
                return (PAPI_BAD_ARGUMENT);

        /* if we are already connected, use that connection. */
        if (svc->connection == NULL)
                if ((result = service_connect(svc, printer)) != PAPI_OK)
                        return (result);

        ipp_initialize_request(svc, &request, type);

        ipp_initialize_operational_attributes(svc, &op, printer, job_id);

        papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
            "operational-attributes-group", op);
        papiAttributeListFree(op);
        result = ipp_send_request(svc, request, &response);
        papiAttributeListFree(request);
        papiAttributeListFree(response);

        return (result);
}

papi_status_t
papiJobCancel(papi_service_t handle, char *printer, int32_t job_id)
{
        return (_job_cancel_hold_release_restart_promote(handle, printer,
            job_id, OPID_CANCEL_JOB));
}


papi_status_t
papiJobHold(papi_service_t handle, char *printer, int32_t job_id)
{
        return (_job_cancel_hold_release_restart_promote(handle, printer,
            job_id, OPID_HOLD_JOB));
}

papi_status_t
papiJobRelease(papi_service_t handle, char *printer, int32_t job_id)
{
        return (_job_cancel_hold_release_restart_promote(handle, printer,
            job_id, OPID_RELEASE_JOB));
}

papi_status_t
papiJobRestart(papi_service_t handle, char *printer, int32_t job_id)
{
        return (_job_cancel_hold_release_restart_promote(handle, printer,
            job_id, OPID_RESTART_JOB));
}

papi_status_t
papiJobPromote(papi_service_t handle, char *printer, int32_t job_id)
{
        return (_job_cancel_hold_release_restart_promote(handle, printer,
            job_id, OPID_PROMOTE_JOB));
}

papi_status_t
papiJobMove(papi_service_t handle, char *printer, int32_t job_id,
                char *destination)
{
        papi_status_t result = PAPI_INTERNAL_ERROR;
        service_t *svc = handle;
        papi_attribute_t **request = NULL, **op = NULL, **response = NULL;

        if ((svc == NULL) || (printer == NULL) || (job_id < 0) ||
            (destination == NULL))
                return (PAPI_BAD_ARGUMENT);

        /* if we are already connected, use that connection. */
        if (svc->connection == NULL)
                if ((result = service_connect(svc, printer)) != PAPI_OK)
                        return (result);

        ipp_initialize_request(svc, &request, OPID_CUPS_MOVE_JOB);

        ipp_initialize_operational_attributes(svc, &op, printer, job_id);

        papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
            "operational-attributes-group", op);
        papiAttributeListFree(op);

        op = NULL;
        papiAttributeListAddString(&op, PAPI_ATTR_EXCL,
            "job-printer-uri", destination);
        papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
            "job-attributes-group", op);
        papiAttributeListFree(op);

        result = ipp_send_request(svc, request, &response);
        papiAttributeListFree(request);
        papiAttributeListFree(response);

        return (result);
}

papi_status_t
papiJobModify(papi_service_t handle, char *printer, int32_t job_id,
                papi_attribute_t **attributes, papi_job_t *job)
{
        papi_status_t result = PAPI_INTERNAL_ERROR;
        service_t *svc = handle;
        papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
        job_t *j = NULL;

        if ((svc == NULL) || (printer == NULL) || (job_id < 0) ||
            (attributes == NULL))
                return (PAPI_BAD_ARGUMENT);

        if ((*job = j = calloc(1, sizeof (*j))) == NULL)
                return (PAPI_TEMPORARY_ERROR);

        /* if we are already connected, use that connection. */
        if (svc->connection == NULL)
                if ((result = service_connect(svc, printer)) != PAPI_OK)
                        return (result);

        ipp_initialize_request(svc, &request, OPID_SET_JOB_ATTRIBUTES);

        ipp_initialize_operational_attributes(svc, &op, printer, job_id);

        papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
            "operational-attributes-group", op);
        papiAttributeListFree(op);
        papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
            "job-attributes-group", attributes);
        result = ipp_send_request(svc, request, &response);
        papiAttributeListFree(request);

        op = NULL;
        papiAttributeListGetCollection(response, NULL,
            "job-attributes-group", &op);
        copy_attributes(&j->attributes, op);
        papiAttributeListFree(response);

        return (result);
}