root/usr/src/cmd/lp/model/lp.tsol_separator.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 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Add TSOL banner, trailer, page header/footers to a print job
 */

/* system header files */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <limits.h>
#include <errno.h>
#include <signal.h>
#include <locale.h>
#include <tsol/label.h>

/* typedefs */

typedef int BOOL;

/* constants */

#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif

#define ME "lp.tsol_separator"
#define POSTSCRIPTLIB "/usr/lib/lp/postscript"
#define SEPARATORPS "tsol_separator.ps"
#define BANNERPS "tsol_banner.ps"
#define TRAILERPS "tsol_trailer.ps"
#define MAXUSERLEN 32
#define MAXHOSTLEN 32

/* external variables */

int     optind;                 /* Used by getopt */
char    *optarg;                /* Used by getopt */

/* prototypes for static functions */

static int ProcessArgs(int argc, char **argv);
static void Usage(void);
static void ParseUsername(char *input, char *user, char *host);
static void EmitPSFile(const char *name);
static BOOL EmitFile(FILE *file);
static void EmitJobData(void);
static void EmitPrologue(void);
static void EmitCommandLineInfo(void);
static void EmitClockBasedInfo(void);
static void EmitLabelInfo(void);
static void CopyStdin(void);

/* static variables */

static char *ArgSeparatorPS;
static char *ArgBannerPS;
static char *ArgTrailerPS;
static char *ArgPSLib;
static char *ArgPrinter;
static char *ArgJobID;
static char *ArgUser;
static char *ArgTitle;
static char *ArgFile;
static BOOL ArgReverse;
static BOOL ArgNoPageLabels;
static int ArgDebugLevel;
static FILE *ArgLogFile;
static m_label_t *FileLabel;
static char *remoteLabel;

int
main(int argc, char *argv[])
{
        int     err;
        /*
         * Run immune from typical interruptions, so that
         * we stand a chance to get the fault message.
         * EOF (or startup error) is the only way out.
         */
        (void) signal(SIGHUP, SIG_IGN);
        (void) signal(SIGINT, SIG_IGN);
        (void) signal(SIGQUIT, SIG_IGN);
        (void) signal(SIGTERM, SIG_IGN);

        (void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
        (void) textdomain(TEXT_DOMAIN);

        if (ProcessArgs(argc, argv) != 0)
                exit(1);

        if ((FileLabel = m_label_alloc(MAC_LABEL)) == NULL)
                exit(1);
        /*
         * If the job was submitted via remotely, the label of the
         * remote peer will be set in the SLABEL environment variable
         * by copying it out of the SECURE structure.
         *
         * If there is no SLABEL value, the job was submitted locally
         * via the named pipe, and the file label can be determined
         * from its pathname.
         */
        if ((remoteLabel = getenv("SLABEL")) != NULL) {
                m_label_free(FileLabel);
                FileLabel = NULL;
                if (str_to_label(remoteLabel, &FileLabel, MAC_LABEL,
                    L_NO_CORRECTION, &err) == -1) {
                        perror("str_to_label");
                        exit(1);
                }
        } else if (getlabel(ArgFile, FileLabel) != 0) {
                (void) fprintf(ArgLogFile,
                    gettext("%1$s: cannot get label of %2$s: %3$s\n"),
                    ME, ArgFile, strerror(errno));
                exit(1);
        }

        /* All of these functions exit if they encounter an error */
        EmitJobData();
        EmitPSFile(ArgSeparatorPS);
        if (ArgReverse)
                EmitPSFile(ArgTrailerPS);
        else
                EmitPSFile(ArgBannerPS);
        CopyStdin();
        if (ArgReverse)
                EmitPSFile(ArgBannerPS);
        else
                EmitPSFile(ArgTrailerPS);
        if (ArgDebugLevel >= 1)
                (void) fprintf(ArgLogFile, gettext("Done.\n"));
        m_label_free(FileLabel);
        return (0);
}

static void
EmitJobData(void)
{
        EmitPrologue();
        EmitCommandLineInfo();
        EmitClockBasedInfo();
        EmitLabelInfo();

        /* Emit ending PostScript code */
        (void) printf("end\n\n");
        (void) printf("%%%% End of code generated by lp.tsol_separator\n\n");

}

static void
EmitPrologue(void)
{
        /* Emit preliminary PostScript code */
        (void) printf("%%!\n\n");
        (void) printf("%%%% Begin code generated by lp.tsol_separator\n\n");

        (void) printf("%%%% Create JobDict if it doesn't exist\n");
        (void) printf("userdict /JobDict known not {\n");
        (void) printf("  userdict /JobDict 100 dict put\n");
        (void) printf("} if\n\n");

        (void) printf("%%%% Define job parameters, including TSOL security "
            "info\n");
        (void) printf("JobDict\n");
        (void) printf("begin\n");
}

/* Emit parameters obtained from command line options */

static void
EmitCommandLineInfo(void)
{
        char user[MAXUSERLEN + 1];
        char host[MAXHOSTLEN + 1];

        (void) printf("\t/Job_Printer (%s) def\n", ArgPrinter);
        ParseUsername(ArgUser, user, host);
        (void) printf("\t/Job_Host (%s) def\n", host);
        (void) printf("\t/Job_User (%s) def\n", user);
        (void) printf("\t/Job_JobID (%s) def\n", ArgJobID);
        (void) printf("\t/Job_Title (%s) def\n", ArgTitle);
        (void) printf("\t/Job_DoPageLabels (%s) def\n",
            ArgNoPageLabels ? "NO" : "YES");
        (void) printf("\n");
}

/* Emit parameters generated from the system clock */

static void
EmitClockBasedInfo(void)
{
        char timebuf[80];
        struct timeval clockval;

        (void) gettimeofday(&clockval, NULL);
        (void) strftime(timebuf, sizeof (timebuf), NULL,
            localtime(&clockval.tv_sec));
        (void) printf("\t/Job_Date (%s) def\n", timebuf);
        (void) printf("\t/Job_Hash (%ld) def\n", clockval.tv_usec % 100000L);
        (void) printf("\n");
}

/* Emit parameters derived from the SL and IL of the file being printed. */

static void
EmitLabelInfo(void)
{
        char    *header = NULL;         /* DIA banner page fields */
        char    *label = NULL;
        char    *caveats = NULL;
        char    *channels = NULL;
        char    *page_label = NULL;     /* interior pages label */

        if (label_to_str(FileLabel, &header, PRINTER_TOP_BOTTOM,
            DEF_NAMES) != 0) {
                (void) fprintf(ArgLogFile,
                    gettext("%s: label_to_str PRINTER_TOP_BOTTOM: %s.\n"),
                    ME, strerror(errno));
                exit(1);
        }
        if (label_to_str(FileLabel, &label, PRINTER_LABEL,
            DEF_NAMES) != 0) {
                (void) fprintf(ArgLogFile,
                    gettext("%s: label_to_str PRINTER_LABEL: %s.\n"),
                    ME, strerror(errno));
                exit(1);
        }
        if (label_to_str(FileLabel, &caveats, PRINTER_CAVEATS,
            DEF_NAMES) != 0) {
                (void) fprintf(ArgLogFile,
                    gettext("%s: label_to_str PRINTER_CAVEATS: %s.\n"),
                    ME, strerror(errno));
                exit(1);
        }
        if (label_to_str(FileLabel, &channels, PRINTER_CHANNELS,
            DEF_NAMES) != 0) {
                (void) fprintf(ArgLogFile,
                    gettext("%s: label_to_str PRINTER_CHANNELS: %s.\n"),
                    ME, strerror(errno));
                exit(1);
        }
        if (label_to_str(FileLabel, &page_label, M_LABEL,
            LONG_NAMES) != 0) {
                (void) fprintf(ArgLogFile,
                    gettext("%s: label_to_str M_LABEL: %s.\n"),
                    ME, strerror(errno));
                exit(1);
        }

        (void) printf("\t/Job_Classification (%s) def\n", header);
        (void) printf("\t/Job_Protect (%s) def\n", label);
        (void) printf("\t/Job_Caveats (%s) def\n", caveats);
        (void) printf("\t/Job_Channels (%s) def\n", channels);
        (void) printf("\t/Job_SL_Internal (%s) def\n", page_label);

        /* Free memory allocated label_to_str */
        free(header);
        free(label);
        free(caveats);
        free(channels);
        free(page_label);
}

/*
 * Parse input "host!user" to separate host and user names.
 */

static void
ParseUsername(char *input, char *user, char *host)
{
        char *cp;

        if ((cp = strchr(input, '@')) != NULL) {
                /* user@host */
                (void) strlcpy(host, cp + 1, MAXHOSTLEN + 1);
                *cp = '\0';
                (void) strlcpy(user, input, MAXUSERLEN + 1);
                *cp = '@';
        } else if ((cp = strchr(input, '!')) != NULL) {
                /* host!user */
                (void) strlcpy(user, cp + 1, MAXUSERLEN + 1);
                *cp = '\0';
                (void) strlcpy(host, input, MAXHOSTLEN + 1);
                *cp = '!';
        } else {
                /* user */
                (void) strlcpy(user, input, MAXUSERLEN + 1);
                host[0] = '\0';
        }
}


static void
CopyStdin(void)
{
        if (!EmitFile(stdin)) {
                (void) fprintf(ArgLogFile,
                    gettext("%s: Error copying stdin to stdout\n"), ME);
                exit(1);
        }
}


static BOOL
EmitFile(FILE *file)
{
        int len;
#define BUFLEN 1024
        char buf[BUFLEN];

        while ((len = fread(buf, 1, BUFLEN, file)) > 0) {
                if (fwrite(buf, 1, len, stdout) != len)
                        return (FALSE);
        }
        if (!feof(file))
                return (FALSE);
        return (TRUE);
}


static void
EmitPSFile(const char *name)
{
        char path[PATH_MAX];
        FILE *file;
        BOOL emitted;

        if (name[0] != '/') {
                (void) strlcpy(path, ArgPSLib, sizeof (path));
                (void) strlcat(path, "/", sizeof (path));
                (void) strlcat(path, name, sizeof (path));
        } else {
                (void) strlcpy(path, name, sizeof (path));
        }

        file = fopen(path, "r");
        if (file == NULL) {
                (void) fprintf(ArgLogFile,
                    gettext("%s: Error opening PostScript file %s. %s.\n"),
                    ME, path, strerror(errno));
                exit(1);
        }

        emitted = EmitFile(file);
        (void) fclose(file);
        if (!emitted) {
                (void) fprintf(ArgLogFile, gettext(
                    "%s: Error copying PostScript file %s to stdout.\n"),
                    ME, path);
                exit(1);
        }
}


static int
ProcessArgs(int argc, char *argv[])
{
        int     option_letter;
        char    *options_string = "lrd:e:s:b:t:L:";

        /* set default values for arguments */
        ArgSeparatorPS = SEPARATORPS;
        ArgBannerPS = BANNERPS;
        ArgTrailerPS = TRAILERPS;
        ArgPSLib = POSTSCRIPTLIB;
        ArgNoPageLabels = ArgReverse = FALSE;
        ArgDebugLevel = 0;
        ArgLogFile = stderr;

        /* read switch arguments once to get error log file */
        while ((option_letter = getopt(argc, argv, options_string)) != EOF) {
                switch (option_letter) {
                case 'd':
                        ArgDebugLevel = atoi(optarg);
                        break;
                case 'e':
                        ArgLogFile = fopen(optarg, "a");
                        if (ArgLogFile == NULL) {
                                (void) fprintf(stderr,
                                    gettext("Cannot open log file %s\n"),
                                    optarg);
                                return (-1);
                        }
                        break;
                case '?':       /* ? or unrecognized option */
                        Usage();
                        return (-1);
                }
        }

        if (ArgDebugLevel > 0)
                (void) fprintf(ArgLogFile,
                    gettext("Processing switch arguments\n"));

        /* re-read switch arguments */
        optind = 1;
        while ((option_letter = getopt(argc, argv, options_string)) != EOF) {
                switch (option_letter) {
                case 'd':
                        ArgDebugLevel = atoi(optarg);
                        break;
                case 'e':
                        /* This was handled in earlier pass through args */
                        break;
                case 'l':
                        ArgNoPageLabels = TRUE;
                        break;
                case 'r':
                        ArgReverse = TRUE;
                        break;
                case 's':
                        ArgSeparatorPS = optarg;
                        break;
                case 'b':
                        ArgBannerPS = optarg;
                        break;
                case 't':
                        ArgTrailerPS = optarg;
                        break;
                case 'L':
                        ArgPSLib = optarg;
                        break;
                case '?':       /* ? or unrecognized option */
                        Usage();
                        return (-1);
                }
        }

        /* Adjust arguments to skip over options */
        argc -= optind;         /* Number of remaining(non-switch) args */
        argv += optind;         /* argv[0] is first(non-switch) args */

        if (argc != 5) {
                (void) fprintf(ArgLogFile,
                    gettext("Wrong number of arguments.\n\n"));
                Usage();
                return (-1);
        }

        ArgPrinter = argv++[0];
        ArgJobID = argv++[0];
        ArgUser = argv++[0];
        ArgTitle = argv++[0];
        ArgFile = argv++[0];

        if (ArgDebugLevel >= 1) {
                (void) fprintf(ArgLogFile, gettext("Arguments processed\n"));
                (void) fprintf(ArgLogFile, gettext("Printer: %s\n"),
                    ArgPrinter);
                (void) fprintf(ArgLogFile, gettext("Job ID: %s\n"), ArgJobID);
                (void) fprintf(ArgLogFile, gettext("User: %s\n"), ArgUser);
                (void) fprintf(ArgLogFile, gettext("Title: %s\n"), ArgTitle);
                (void) fprintf(ArgLogFile, gettext("File: %s\n"), ArgFile);
        }

        return (0);
}


static void
Usage(void)
{
        static const char *OPTFMT = "    %-8s %-9s %s\n";

        (void) fprintf(ArgLogFile,
            gettext("Usage:  lp.tsol_separator [OPTIONS] %s\n"),
            gettext("PRINTER JOBID HOST!USER TITLE FILE"));
        (void) fprintf(ArgLogFile, gettext("  OPTIONS:\n"));
        (void) fprintf(ArgLogFile, OPTFMT, "-r", gettext("Reverse"),
            gettext("Reverse banner/trailer order"));
        (void) fprintf(ArgLogFile, OPTFMT, "-l", gettext("Labels"),
            gettext("Suppress page header/footer labels"));
        (void) fprintf(ArgLogFile, OPTFMT, gettext("-b FILE"),
            gettext("Banner"),
            gettext("PostScript program for banner (default tsol_banner.ps)"));
        (void) fprintf(ArgLogFile, OPTFMT, gettext("-s FILE"),
            gettext("Separator"),
            gettext("PostScript program for separator "
            "(default tsol_separator.ps)"));
        (void) fprintf(ArgLogFile, OPTFMT, gettext("-t FILE"),
            gettext("Trailer"),
            gettext("PostScript program for trailer "
            "(default tsol_trailer.ps)"));
        (void) fprintf(ArgLogFile, OPTFMT, gettext("-L DIR"),
            gettext("Library"),
            gettext("Directory to search for PostScript programs"));
        (void) fprintf(ArgLogFile, OPTFMT, "", "",
            gettext("(default /usr/lib/lp/postscript)"));
        (void) fprintf(ArgLogFile, OPTFMT, gettext("-d N"), gettext("Debug"),
            gettext("Set debug level to N"));
        (void) fprintf(ArgLogFile, OPTFMT, gettext("-e FILE"),
            gettext("Error File"),
            gettext("Append error and debugging output to FILE"));
}