## Overview

The CUPS API provides the convenience functions needed to support applications, filters, printer drivers, and backends that need to interface with the CUPS scheduler.

### Clients and Servers

CUPS is based on the Internet Printing Protocol ("IPP"), which allows clients (applications) to communicate with a server (the scheduler) to get a list of printers, send print jobs, and so forth. You identify which server you want to communicate with using a pointer to the opaque structure `http_t`. All of the examples in this document use the `CUPS_HTTP_DEFAULT` constant, referring to the default connection to the scheduler. The HTTP and IPP APIs document provides more information on server connections.

### Printers and Classes

Printers and classes (collections of printers) are accessed through the `cups_dest_t` structure which includes the name (`name`), instance (`instance` - a way of selecting certain saved options/settings), and the options and attributes associated with that destination (`num_options` and `options`). Destinations are created using the `cupsGetDests` function and freed using the `cupsFreeDests` function. The `cupsGetDest` function finds a specific destination for printing:

    #include <cups/cups.h>

    cups_dest_t *dests;
    int num_dests = cupsGetDests(&dests);
    cups_dest_t *dest = cupsGetDest("name", NULL, num_dests, dests);

    /* do something with dest */

    cupsFreeDests(num_dests, dests);

Passing `NULL` to `cupsGetDest` for the destination name will return the default destination. Similarly, passing a `NULL` instance will return the default instance for that destination.

Table 1: Printer Attributes
Attribute Name | Description
--- | ---
"auth-info-required" | The type of authentication required for printing to this destination: "none", "username,password", "domain,username,password", or "negotiate" (Kerberos)
"printer-info" | The human-readable description of the destination such as "My Laser Printer".
"printer-is-accepting-jobs" | "true" if the destination is accepting new jobs, "false" if not.
"printer-is-shared" | "true" if the destination is being shared with other computers, "false" if not.
"printer-location" | The human-readable location of the destination such as "Lab 4".
"printer-make-and-model" | The human-readable make and model of the destination such as "HP LaserJet 4000 Series".
"printer-state" | "3" if the destination is idle, "4" if the destination is printing a job, and "5" if the destination is stopped.
"printer-state-change-time" | The UNIX time when the destination entered the current state.
"printer-state-reasons" | Additional comma-delimited state keywords for the destination such as "media-tray-empty-error" and "toner-low-warning".
"printer-type" | The `cups_printer_t` value associated with the destination.

### Options

Options are stored in arrays of `cups_option_t` structures. Each option has a name (`name`) and value (`value`) associated with it. The `cups_dest_t` `num_options` and `options` members contain the default options for a particular destination, along with several informational attributes about the destination as shown in Table 1. The `cupsGetOption` function gets the value for the named option. For example, the following code lists the available destinations and their human-readable descriptions:

    #include <cups/cups.h>

    cups_dest_t *dests;
    int num_dests = cupsGetDests(&dests);
    cups_dest_t *dest;
    int i;
    const char *value;

    for (i = num_dests, dest = dests; i > 0; i --, dest ++)
      if (dest->instance == NULL)
      {
        value = cupsGetOption("printer-info", dest->num_options, dest->options);
        printf("%s (%s)\n", dest->name, value ? value : "no description");
      }

    cupsFreeDests(num_dests, dests);

You can create your own option arrays using the `cupsAddOption` function, which adds a single named option to an array:

    #include <cups/cups.h>

    int num_options = 0;
    cups_option_t *options = NULL;

    /* The returned num_options value is updated as needed */
    num_options = cupsAddOption("first", "value", num_options, &options);

    /* This adds a second option value */
    num_options = cupsAddOption("second", "value", num_options, &options);

    /* This replaces the first option we added */
    num_options = cupsAddOption("first", "new value", num_options, &options);

Use a `for` loop to copy the options from a destination:

    #include <cups/cups.h>

    int i;
    int num_options = 0;
    cups_option_t *options = NULL;
    cups_dest_t *dest;

    for (i = 0; i < dest->num_options; i ++)
      num_options = cupsAddOption(dest->options[i].name, dest->options[i].value,
                                  num_options, &options);

Use the `cupsFreeOptions` function to free the options array when you are done using it:

    cupsFreeOptions(num_options, options);

### Print Jobs

Print jobs are identified by a locally-unique job ID number from 1 to 231-1 and have options and one or more files for printing to a single destination. The `cupsPrintFile` function creates a new job with one file. The following code prints the CUPS test page file:

    #include <cups/cups.h>

    cups_dest_t *dest;
    int num_options;
    cups_option_t *options;
    int job_id;

    /* Print a single file */
    job_id = cupsPrintFile(dest->name, "/usr/share/cups/data/testprint.ps",
                            "Test Print", num_options, options);

The `cupsPrintFiles` function creates a job with multiple files. The files are provided in a `char *` array:

    #include <cups/cups.h>

    cups_dest_t *dest;
    int num_options;
    cups_option_t *options;
    int job_id;
    char *files[3] = { "file1.pdf", "file2.pdf", "file3.pdf" };

    /* Print three files */
    job_id = cupsPrintFiles(dest->name, 3, files, "Test Print", num_options, options);

Finally, the `cupsCreateJob` function creates a new job with no files in it. Files are added using the `cupsStartDocument`, `cupsWriteRequestData`, and `cupsFinishDocument` functions. The following example creates a job with 10 text files for printing:

    #include <cups/cups.h>

    cups_dest_t *dest;
    int num_options;
    cups_option_t *options;
    int job_id;
    int i;
    char buffer[1024];

    /* Create the job */
    job_id = cupsCreateJob(CUPS_HTTP_DEFAULT, dest->name, "10 Text Files",
                           num_options, options);

    /* If the job is created, add 10 files */
    if (job_id > 0)
    {
      for (i = 1; i <= 10; i ++)
      {
        snprintf(buffer, sizeof(buffer), "file%d.txt", i);

        cupsStartDocument(CUPS_HTTP_DEFAULT, dest->name, job_id, buffer,
                          CUPS_FORMAT_TEXT, i == 10);

        snprintf(buffer, sizeof(buffer),
                 "File %d\n"
                 "\n"
                 "One fish,\n"
                 "Two fish,\n
                 "Red fish,\n
                 "Blue fish\n", i);

        /* cupsWriteRequestData can be called as many times as needed */
        cupsWriteRequestData(CUPS_HTTP_DEFAULT, buffer, strlen(buffer));

        cupsFinishDocument(CUPS_HTTP_DEFAULT, dest->name);
      }
    }

Once you have created a job, you can monitor its status using the `cupsGetJobs` function, which returns an array of `cups_job_t` structures. Each contains the job ID (`id`), destination name (`dest`), title (`title`), and other information associated with the job. The job array is freed using the `cupsFreeJobs` function. The following example monitors a specific job ID, showing the current job state once every 5 seconds until the job is completed:

    #include <cups/cups.h>

    cups_dest_t *dest;
    int job_id;
    int num_jobs;
    cups_job_t *jobs;
    int i;
    ipp_jstate_t job_state = IPP_JOB_PENDING;

    while (job_state < IPP_JOB_STOPPED)
    {
      /* Get my jobs (1) with any state (-1) */
      num_jobs = cupsGetJobs(&jobs, dest->name, 1, -1);

      /* Loop to find my job */
      job_state = IPP_JOB_COMPLETED;

      for (i = 0; i < num_jobs; i ++)
        if (jobs[i].id == job_id)
        {
          job_state = jobs[i].state;
          break;
        }

      /* Free the job array */
      cupsFreeJobs(num_jobs, jobs);

      /* Show the current state */
      switch (job_state)
      {
        case IPP_JOB_PENDING :
            printf("Job %d is pending.\n", job_id);
            break;
        case IPP_JOB_HELD :
            printf("Job %d is held.\n", job_id);
            break;
        case IPP_JOB_PROCESSING :
            printf("Job %d is processing.\n", job_id);
            break;
        case IPP_JOB_STOPPED :
            printf("Job %d is stopped.\n", job_id);
            break;
        case IPP_JOB_CANCELED :
            printf("Job %d is canceled.\n", job_id);
            break;
        case IPP_JOB_ABORTED :
            printf("Job %d is aborted.\n", job_id);
            break;
        case IPP_JOB_COMPLETED :
            printf("Job %d is completed.\n", job_id);
            break;
      }

      /* Sleep if the job is not finished */
      if (job_state < IPP_JOB_STOPPED)
        sleep(5);
    }

To cancel a job, use the `cupsCancelJob` function with the job ID:

    #include <cups/cups.h>

    cups_dest_t *dest;
    int job_id;

    cupsCancelJob(dest->name, job_id);

### Error Handling

If any of the CUPS API printing functions returns an error, the reason for that error can be found by calling the `cupsLastError` and `cupsLastErrorString` functions. `cupsLastError` returns the last IPP error code (`ipp_status_t`) that was encountered, while `cupsLastErrorString` returns a (localized) human-readable string that can be shown to the user. For example, if any of the job creation functions returns a job ID of 0, you can use `cupsLastErrorString` to show the reason why the job could not be created:

    #include <cups/cups.h>

    int job_id;

    if (job_id == 0)
      puts(cupsLastErrorString());

### Passwords and Authentication

CUPS supports authentication of any request, including submission of print jobs. The default mechanism for getting the username and password is to use the login user and a password from the console.

To support other types of applications, in particular Graphical User Interfaces ("GUIs"), the CUPS API provides functions to set the default username and to register a callback function that returns a password string.

The `cupsSetPasswordCB` function is used to set a password callback in your program. Only one function can be used at any time.

The `cupsSetUser` function sets the current username for authentication. This function can be called by your password callback function to change the current username as needed.

The following example shows a simple password callback that gets a username and password from the user:

    #include <cups/cups.h>

    const char *
    my_password_cb(const char *prompt)
    {
      char	user[65];

      puts(prompt);

      /* Get a username from the user */
      printf("Username: ");
      if (fgets(user, sizeof(user), stdin) == NULL)
        return (NULL);

      /* Strip the newline from the string and set the user */
      user[strlen(user) - 1] = '\0';

      cupsSetUser(user);

      /* Use getpass() to ask for the password... */
      return (getpass("Password: "));
    }

    cupsSetPasswordCB(my_password_cb);

Similarly, a GUI could display the prompt string in a window with input fields for the username and password. The username should default to the string returned by the `cupsUser` function.