2 * "$Id: cups-lpd.c 10379 2012-03-23 22:16:22Z mike $"
4 * Line Printer Daemon interface for CUPS.
6 * Copyright 2007-2011 by Apple Inc.
7 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
17 * main() - Process an incoming LPD request...
18 * create_job() - Create a new print job.
19 * get_printer() - Get the named printer and its options.
20 * print_file() - Add a file to the current job.
21 * recv_print_job() - Receive a print job from the client.
22 * remove_jobs() - Cancel one or more jobs.
23 * send_state() - Send the queue state.
24 * smart_gets() - Get a line of text, removing the trailing CR and/or LF.
28 * Include necessary headers...
31 #include <cups/cups-private.h>
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
40 #ifdef HAVE_INTTYPES_H
41 # include <inttypes.h>
42 #endif /* HAVE_INTTYPES_H */
46 * LPD "mini-daemon" for CUPS. This program must be used in conjunction
47 * with inetd or another similar program that monitors ports and starts
48 * daemons for each client connection. A typical configuration is:
50 * printer stream tcp nowait lp /usr/lib/cups/daemon/cups-lpd cups-lpd
52 * This daemon implements most of RFC 1179 (the unofficial LPD specification)
55 * - This daemon does not check to make sure that the source port is
56 * between 721 and 731, since it isn't necessary for proper
57 * functioning and port-based security is no security at all!
59 * - The "Print any waiting jobs" command is a no-op.
61 * The LPD-to-IPP mapping is as defined in RFC 2569. The report formats
62 * currently match the Solaris LPD mini-daemon.
69 static int create_job(http_t *http, const char *dest, const char *title,
70 const char *docname, const char *user,
71 int num_options, cups_option_t *options);
72 static int get_printer(http_t *http, const char *name, char *dest,
73 int destsize, cups_option_t **options,
74 int *accepting, int *shared, ipp_pstate_t *state);
75 static int print_file(http_t *http, int id, const char *filename,
76 const char *docname, const char *user,
77 const char *format, int last);
78 static int recv_print_job(const char *name, int num_defaults,
79 cups_option_t *defaults);
80 static int remove_jobs(const char *name, const char *agent,
82 static int send_state(const char *name, const char *list,
84 static char *smart_gets(char *s, int len, FILE *fp);
88 * 'main()' - Process an incoming LPD request...
91 int /* O - Exit status */
92 main(int argc, /* I - Number of command-line arguments */
93 char *argv[]) /* I - Command-line arguments */
95 int i; /* Looping var */
96 int num_defaults; /* Number of default options */
97 cups_option_t *defaults; /* Default options */
98 char line[256], /* Command string */
99 command, /* Command code */
100 *dest, /* Pointer to destination */
101 *list, /* Pointer to list */
102 *agent, /* Pointer to user */
103 status; /* Status for client */
104 socklen_t hostlen; /* Size of client address */
105 http_addr_t hostaddr; /* Address of client */
106 char hostname[256], /* Name of client */
107 hostip[256], /* IP address */
108 *hostfamily; /* Address family */
109 int hostlookups; /* Do hostname lookups? */
113 * Don't buffer the output...
116 setbuf(stdout, NULL);
119 * Log things using the "cups-lpd" name...
122 openlog("cups-lpd", LOG_PID, LOG_LPR);
125 * Scan the command-line for options...
132 for (i = 1; i < argc; i ++)
133 if (argv[i][0] == '-')
137 case 'h' : /* -h hostname[:port] */
139 cupsSetServer(argv[i] + 2);
144 cupsSetServer(argv[i]);
146 syslog(LOG_WARNING, "Expected hostname string after -h option!");
150 case 'o' : /* Option */
152 num_defaults = cupsParseOptions(argv[i] + 2, num_defaults,
158 num_defaults = cupsParseOptions(argv[i], num_defaults,
161 syslog(LOG_WARNING, "Expected option string after -o option!");
165 case 'n' : /* Don't do hostname lookups */
170 syslog(LOG_WARNING, "Unknown option \"%c\" ignored!", argv[i][1]);
175 syslog(LOG_WARNING, "Unknown command-line option \"%s\" ignored!",
179 * Get the address of the client...
182 hostlen = sizeof(hostaddr);
184 if (getpeername(0, (struct sockaddr *)&hostaddr, &hostlen))
186 syslog(LOG_WARNING, "Unable to get client address - %s", strerror(errno));
187 strcpy(hostname, "unknown");
191 httpAddrString(&hostaddr, hostip, sizeof(hostip));
194 httpAddrLookup(&hostaddr, hostname, sizeof(hostname));
196 strlcpy(hostname, hostip, sizeof(hostname));
199 if (hostaddr.addr.sa_family == AF_INET6)
202 #endif /* AF_INET6 */
205 syslog(LOG_INFO, "Connection from %s (%s %s)", hostname, hostfamily,
209 num_defaults = cupsAddOption("job-originating-host-name", hostname,
210 num_defaults, &defaults);
213 * RFC1179 specifies that only 1 daemon command can be received for
217 if (smart_gets(line, sizeof(line), stdin) == NULL)
220 * Unable to get command from client! Send an error status and return.
223 syslog(LOG_ERR, "Unable to get command line from client!");
229 * The first byte is the command byte. After that will be the queue name,
230 * resource list, and/or user name.
240 for (list = dest; *list && !isspace(*list & 255); list ++);
242 while (isspace(*list & 255))
252 default : /* Unknown command */
253 syslog(LOG_ERR, "Unknown LPD command 0x%02X!", command);
254 syslog(LOG_ERR, "Command line = %s", line + 1);
260 case 0x01 : /* Print any waiting jobs */
261 syslog(LOG_INFO, "Print waiting jobs (no-op)");
267 case 0x02 : /* Receive a printer job */
268 syslog(LOG_INFO, "Receive print job for %s", dest);
269 /* recv_print_job() sends initial status byte */
271 status = recv_print_job(dest, num_defaults, defaults);
274 case 0x03 : /* Send queue state (short) */
275 syslog(LOG_INFO, "Send queue state (short) for %s %s", dest, list);
276 /* no status byte for this command */
278 status = send_state(dest, list, 0);
281 case 0x04 : /* Send queue state (long) */
282 syslog(LOG_INFO, "Send queue state (long) for %s %s", dest, list);
283 /* no status byte for this command */
285 status = send_state(dest, list, 1);
288 case 0x05 : /* Remove jobs */
292 * Grab the agent and skip to the list of users and/or jobs.
297 for (; *list && !isspace(*list & 255); list ++);
298 while (isspace(*list & 255))
301 syslog(LOG_INFO, "Remove jobs %s on %s by %s", list, dest, agent);
303 status = remove_jobs(dest, agent, list);
312 syslog(LOG_INFO, "Closing connection");
320 * 'create_job()' - Create a new print job.
323 static int /* O - Job ID or -1 on error */
324 create_job(http_t *http, /* I - HTTP connection */
325 const char *dest, /* I - Destination name */
326 const char *title, /* I - job-name */
327 const char *docname, /* I - Name of job file */
328 const char *user, /* I - requesting-user-name */
329 int num_options, /* I - Number of options for job */
330 cups_option_t *options) /* I - Options for job */
332 ipp_t *request; /* IPP request */
333 ipp_t *response; /* IPP response */
334 ipp_attribute_t *attr; /* IPP attribute */
335 char uri[HTTP_MAX_URI]; /* Printer URI */
340 * Setup the Create-Job request...
343 request = ippNewRequest(IPP_CREATE_JOB);
345 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
346 "localhost", 0, "/printers/%s", dest);
348 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
351 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
352 "requesting-user-name", NULL, user);
355 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name",
359 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name",
362 cupsEncodeOptions(request, num_options, options);
368 snprintf(uri, sizeof(uri), "/printers/%s", dest);
370 response = cupsDoRequest(http, request, uri);
372 if (!response || cupsLastError() > IPP_OK_CONFLICT)
374 syslog(LOG_ERR, "Unable to create job - %s", cupsLastErrorString());
382 * Get the job-id value from the response and return it...
385 if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL)
389 syslog(LOG_ERR, "No job-id attribute found in response from server!");
393 id = attr->values[0].integer;
395 syslog(LOG_INFO, "Print file - job ID = %d", id);
405 * 'get_printer()' - Get the named printer and its options.
408 static int /* O - Number of options or -1 on error */
409 get_printer(http_t *http, /* I - HTTP connection */
410 const char *name, /* I - Printer name from request */
411 char *dest, /* I - Destination buffer */
412 int destsize, /* I - Size of destination buffer */
413 cups_option_t **options, /* O - Printer options */
414 int *accepting, /* O - printer-is-accepting-jobs value */
415 int *shared, /* O - printer-is-shared value */
416 ipp_pstate_t *state) /* O - printer-state value */
418 int num_options; /* Number of options */
419 cups_file_t *fp; /* lpoptions file */
420 char line[1024], /* Line from lpoptions file */
421 *value, /* Pointer to value on line */
422 *optptr; /* Pointer to options on line */
423 int linenum; /* Line number in file */
424 const char *cups_serverroot; /* CUPS_SERVERROOT env var */
425 ipp_t *request; /* IPP request */
426 ipp_t *response; /* IPP response */
427 ipp_attribute_t *attr; /* IPP attribute */
428 char uri[HTTP_MAX_URI]; /* Printer URI */
429 static const char * const requested[] =
430 { /* Requested attributes */
432 "printer-is-accepting-jobs",
440 * Initialize everything...
448 *state = IPP_PRINTER_STOPPED;
453 * See if the name is a queue name optionally with an instance name.
456 strlcpy(dest, name, destsize);
457 if ((value = strchr(dest, '/')) != NULL)
461 * Setup the Get-Printer-Attributes request...
464 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
466 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
467 "localhost", 0, "/printers/%s", dest);
469 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
472 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
473 "requested-attributes",
474 (int)(sizeof(requested) / sizeof(requested[0])),
481 response = cupsDoRequest(http, request, "/");
483 if (!response || cupsLastError() > IPP_OK_CONFLICT)
486 * If we can't find the printer by name, look up the printer-name
487 * using the printer-info values...
490 ipp_attribute_t *accepting_attr,/* printer-is-accepting-jobs */
491 *info_attr, /* printer-info */
492 *name_attr, /* printer-name */
493 *shared_attr, /* printer-is-shared */
494 *state_attr; /* printer-state */
500 * Setup the CUPS-Get-Printers request...
503 request = ippNewRequest(CUPS_GET_PRINTERS);
505 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
506 "requested-attributes",
507 (int)(sizeof(requested) / sizeof(requested[0])),
514 response = cupsDoRequest(http, request, "/");
516 if (!response || cupsLastError() > IPP_OK_CONFLICT)
518 syslog(LOG_ERR, "Unable to get list of printers - %s",
519 cupsLastErrorString());
527 * Scan the response for printers...
531 attr = response->attrs;
536 * Skip to the next printer...
539 while (attr && attr->group_tag != IPP_TAG_PRINTER)
546 * Get all of the attributes for the current printer...
549 accepting_attr = NULL;
555 while (attr && attr->group_tag == IPP_TAG_PRINTER)
557 if (!strcmp(attr->name, "printer-is-accepting-jobs") &&
558 attr->value_tag == IPP_TAG_BOOLEAN)
559 accepting_attr = attr;
560 else if (!strcmp(attr->name, "printer-info") &&
561 attr->value_tag == IPP_TAG_TEXT)
563 else if (!strcmp(attr->name, "printer-name") &&
564 attr->value_tag == IPP_TAG_NAME)
566 else if (!strcmp(attr->name, "printer-is-shared") &&
567 attr->value_tag == IPP_TAG_BOOLEAN)
569 else if (!strcmp(attr->name, "printer-state") &&
570 attr->value_tag == IPP_TAG_ENUM)
576 if (info_attr && name_attr &&
577 !_cups_strcasecmp(name, info_attr->values[0].string.text))
580 * Found a match, use this one!
583 strlcpy(dest, name_attr->values[0].string.text, destsize);
585 if (accepting && accepting_attr)
586 *accepting = accepting_attr->values[0].boolean;
588 if (shared && shared_attr)
589 *shared = shared_attr->values[0].boolean;
591 if (state && state_attr)
592 *state = (ipp_pstate_t)state_attr->values[0].integer;
602 syslog(LOG_ERR, "Unable to find \"%s\" in list of printers!", name);
612 * Get values from the response...
617 if ((attr = ippFindAttribute(response, "printer-is-accepting-jobs",
618 IPP_TAG_BOOLEAN)) == NULL)
619 syslog(LOG_ERR, "No printer-is-accepting-jobs attribute found in "
620 "response from server!");
622 *accepting = attr->values[0].boolean;
627 if ((attr = ippFindAttribute(response, "printer-is-shared",
628 IPP_TAG_BOOLEAN)) == NULL)
630 syslog(LOG_ERR, "No printer-is-shared attribute found in "
631 "response from server!");
635 *shared = attr->values[0].boolean;
640 if ((attr = ippFindAttribute(response, "printer-state",
641 IPP_TAG_ENUM)) == NULL)
642 syslog(LOG_ERR, "No printer-state attribute found in "
643 "response from server!");
645 *state = (ipp_pstate_t)attr->values[0].integer;
652 * Next look for the printer in the lpoptions file...
657 if (options && shared && accepting)
659 if ((cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
660 cups_serverroot = CUPS_SERVERROOT;
662 snprintf(line, sizeof(line), "%s/lpoptions", cups_serverroot);
663 if ((fp = cupsFileOpen(line, "r")) != NULL)
666 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
669 * Make sure we have "Dest name options" or "Default name options"...
672 if ((_cups_strcasecmp(line, "Dest") && _cups_strcasecmp(line, "Default")) || !value)
676 * Separate destination name from options...
679 for (optptr = value; *optptr && !isspace(*optptr & 255); optptr ++);
681 while (*optptr == ' ')
685 * If this is our destination, parse the options and break out of
686 * the loop - we're done!
689 if (!_cups_strcasecmp(value, name))
691 num_options = cupsParseOptions(optptr, num_options, options);
703 * Return the number of options for this destination...
706 return (num_options);
711 * 'print_file()' - Add a file to the current job.
714 static int /* O - 0 on success, -1 on failure */
715 print_file(http_t *http, /* I - HTTP connection */
716 int id, /* I - Job ID */
717 const char *filename, /* I - File to print */
718 const char *docname, /* I - document-name */
719 const char *user, /* I - requesting-user-name */
720 const char *format, /* I - document-format */
721 int last) /* I - 1 = last file in job */
723 ipp_t *request; /* IPP request */
724 char uri[HTTP_MAX_URI]; /* Printer URI */
728 * Setup the Send-Document request...
731 request = ippNewRequest(IPP_SEND_DOCUMENT);
733 snprintf(uri, sizeof(uri), "ipp://localhost/jobs/%d", id);
734 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
736 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
737 "requesting-user-name", NULL, user);
740 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
741 "document-name", NULL, docname);
744 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_MIMETYPE,
745 "document-format", NULL, format);
748 ippAddBoolean(request, IPP_TAG_OPERATION, "last-document", 1);
754 snprintf(uri, sizeof(uri), "/jobs/%d", id);
756 ippDelete(cupsDoFileRequest(http, request, uri, filename));
758 if (cupsLastError() > IPP_OK_CONFLICT)
760 syslog(LOG_ERR, "Unable to send document - %s", cupsLastErrorString());
770 * 'recv_print_job()' - Receive a print job from the client.
773 static int /* O - Command status */
775 const char *queue, /* I - Printer name */
776 int num_defaults, /* I - Number of default options */
777 cups_option_t *defaults) /* I - Default options */
779 http_t *http; /* HTTP connection */
780 int i; /* Looping var */
781 int status; /* Command status */
782 int fd; /* Temporary file */
783 FILE *fp; /* File pointer */
784 char filename[1024]; /* Temporary filename */
785 int bytes; /* Bytes received */
786 char line[256], /* Line from file/stdin */
787 command, /* Command from line */
788 *count, /* Number of bytes */
789 *name; /* Name of file */
790 const char *job_sheets; /* Job sheets */
791 int num_data; /* Number of data files */
792 char control[1024], /* Control filename */
793 data[100][256], /* Data files */
794 temp[100][1024]; /* Temporary files */
795 char user[1024], /* User name */
796 title[1024], /* Job title */
797 docname[1024], /* Document name */
798 dest[256]; /* Printer/class queue */
799 int accepting, /* printer-is-accepting */
800 shared, /* printer-is-shared */
801 num_options; /* Number of options */
802 cups_option_t *options; /* Options */
804 int docnumber, /* Current document number */
805 doccount; /* Count of documents */
809 * Connect to the server...
812 http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
815 syslog(LOG_ERR, "Unable to connect to server: %s", strerror(errno));
823 * See if the printer is available...
826 num_options = get_printer(http, queue, dest, sizeof(dest), &options,
827 &accepting, &shared, NULL);
829 if (num_options < 0 || !accepting || !shared)
832 syslog(LOG_INFO, "Rejecting job because \"%s\" is not %s", dest,
833 !accepting ? "accepting jobs" : "shared");
835 syslog(LOG_ERR, "Unable to get printer information for \"%s\"", queue);
844 putchar(0); /* OK so far... */
847 * Read the request...
856 while (smart_gets(line, sizeof(line), stdin) != NULL)
858 if (strlen(line) < 2)
867 for (name = count + 1; *name && !isspace(*name & 255); name ++);
868 while (isspace(*name & 255))
874 case 0x01 : /* Abort */
878 case 0x02 : /* Receive control file */
879 if (strlen(name) < 2)
881 syslog(LOG_ERR, "Bad control file name \"%s\"", name);
890 * Append to the existing control file - the LPD spec is
891 * not entirely clear, but at least the OS/2 LPD code sends
892 * multiple control files per connection...
895 if ((fd = open(control, O_WRONLY)) < 0)
898 "Unable to append to temporary control file \"%s\" - %s",
899 control, strerror(errno));
905 lseek(fd, 0, SEEK_END);
909 if ((fd = cupsTempFd(control, sizeof(control))) < 0)
911 syslog(LOG_ERR, "Unable to open temporary control file \"%s\" - %s",
912 control, strerror(errno));
918 strcpy(filename, control);
922 case 0x03 : /* Receive data file */
923 if (strlen(name) < 2)
925 syslog(LOG_ERR, "Bad data file name \"%s\"", name);
931 if (num_data >= (int)(sizeof(data) / sizeof(data[0])))
934 * Too many data files...
937 syslog(LOG_ERR, "Too many data files (%d)", num_data);
943 strlcpy(data[num_data], name, sizeof(data[0]));
945 if ((fd = cupsTempFd(temp[num_data], sizeof(temp[0]))) < 0)
947 syslog(LOG_ERR, "Unable to open temporary data file \"%s\" - %s",
948 temp[num_data], strerror(errno));
954 strcpy(filename, temp[num_data]);
966 * Copy the data or control file from the client...
969 for (i = atoi(count); i > 0; i -= bytes)
971 if (i > sizeof(line))
972 bytes = sizeof(line);
976 if ((bytes = fread(line, 1, bytes, stdin)) > 0)
977 bytes = write(fd, line, bytes);
981 syslog(LOG_ERR, "Error while reading file - %s",
989 * Read trailing nul...
994 if (fread(line, 1, 1, stdin) < 1)
997 syslog(LOG_ERR, "Error while reading trailing nul - %s",
1003 syslog(LOG_ERR, "Trailing character after file is not nul (%02X)!",
1009 * Close the file and send an acknowledgement...
1023 * Process the control file and print stuff...
1026 if ((fp = fopen(control, "rb")) == NULL)
1031 * Copy the default options...
1034 for (i = 0; i < num_defaults; i ++)
1035 num_options = cupsAddOption(defaults[i].name,
1037 num_options, &options);
1040 * Grab the job information...
1048 while (smart_gets(line, sizeof(line), fp) != NULL)
1051 * Process control lines...
1056 case 'J' : /* Job name */
1057 strlcpy(title, line + 1, sizeof(title));
1060 case 'N' : /* Document name */
1061 strlcpy(docname, line + 1, sizeof(docname));
1064 case 'P' : /* User identification */
1065 strlcpy(user, line + 1, sizeof(user));
1068 case 'L' : /* Print banner page */
1070 * If a banner was requested and it's not overridden by a
1071 * command line option and the destination's default is none
1072 * then add the standard banner...
1075 if (cupsGetOption("job-sheets", num_defaults, defaults) == NULL &&
1076 ((job_sheets = cupsGetOption("job-sheets", num_options,
1077 options)) == NULL ||
1078 !strcmp(job_sheets, "none,none")))
1080 num_options = cupsAddOption("job-sheets", "standard",
1081 num_options, &options);
1085 case 'c' : /* Plot CIF file */
1086 case 'd' : /* Print DVI file */
1087 case 'f' : /* Print formatted file */
1088 case 'g' : /* Plot file */
1089 case 'l' : /* Print file leaving control characters (raw) */
1090 case 'n' : /* Print ditroff output file */
1091 case 'o' : /* Print PostScript output file */
1092 case 'p' : /* Print file with 'pr' format (prettyprint) */
1093 case 'r' : /* File to print with FORTRAN carriage control */
1094 case 't' : /* Print troff output file */
1095 case 'v' : /* Print raster file */
1098 if (line[0] == 'l' &&
1099 !cupsGetOption("document-format", num_options, options))
1100 num_options = cupsAddOption("raw", "", num_options, &options);
1103 num_options = cupsAddOption("prettyprint", "", num_options,
1113 * Check that we have a username...
1118 syslog(LOG_WARNING, "No username specified by client! "
1119 "Using \"anonymous\"...");
1120 strcpy(user, "anonymous");
1127 if ((id = create_job(http, dest, title, docname, user, num_options,
1133 * Then print the job files...
1141 while (smart_gets(line, sizeof(line), fp) != NULL)
1144 * Process control lines...
1149 case 'N' : /* Document name */
1150 strlcpy(docname, line + 1, sizeof(docname));
1153 case 'c' : /* Plot CIF file */
1154 case 'd' : /* Print DVI file */
1155 case 'f' : /* Print formatted file */
1156 case 'g' : /* Plot file */
1157 case 'l' : /* Print file leaving control characters (raw) */
1158 case 'n' : /* Print ditroff output file */
1159 case 'o' : /* Print PostScript output file */
1160 case 'p' : /* Print file with 'pr' format (prettyprint) */
1161 case 'r' : /* File to print with FORTRAN carriage control */
1162 case 't' : /* Print troff output file */
1163 case 'v' : /* Print raster file */
1165 * Figure out which file we are printing...
1168 for (i = 0; i < num_data; i ++)
1169 if (!strcmp(data[i], line + 1))
1179 * Send the print file...
1184 if (print_file(http, id, temp[i], docname, user,
1185 cupsGetOption("document-format", num_options,
1187 docnumber == doccount))
1204 cupsFreeOptions(num_options, options);
1209 * Clean up all temporary files and return...
1214 for (i = 0; i < num_data; i ++)
1222 * 'remove_jobs()' - Cancel one or more jobs.
1225 static int /* O - Command status */
1226 remove_jobs(const char *dest, /* I - Destination */
1227 const char *agent, /* I - User agent */
1228 const char *list) /* I - List of jobs or users */
1230 int id; /* Job ID */
1231 http_t *http; /* HTTP server connection */
1232 ipp_t *request; /* IPP Request */
1233 char uri[HTTP_MAX_URI]; /* Job URI */
1236 (void)dest; /* Suppress compiler warnings... */
1239 * Try connecting to the local server...
1242 if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
1243 cupsEncryption())) == NULL)
1245 syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(),
1251 * Loop for each job...
1254 while ((id = atoi(list)) > 0)
1257 * Skip job ID in list...
1260 while (isdigit(*list & 255))
1262 while (isspace(*list & 255))
1266 * Build an IPP_CANCEL_JOB request, which requires the following
1269 * attributes-charset
1270 * attributes-natural-language
1272 * requesting-user-name
1275 request = ippNewRequest(IPP_CANCEL_JOB);
1277 sprintf(uri, "ipp://localhost/jobs/%d", id);
1278 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri);
1280 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1281 "requesting-user-name", NULL, agent);
1284 * Do the request and get back a response...
1287 ippDelete(cupsDoRequest(http, request, "/jobs"));
1289 if (cupsLastError() > IPP_OK_CONFLICT)
1291 syslog(LOG_WARNING, "Cancel of job ID %d failed: %s\n", id,
1292 cupsLastErrorString());
1297 syslog(LOG_INFO, "Job ID %d canceled", id);
1307 * 'send_state()' - Send the queue state.
1310 static int /* O - Command status */
1311 send_state(const char *queue, /* I - Destination */
1312 const char *list, /* I - Job or user */
1313 int longstatus) /* I - List of jobs or users */
1315 int id; /* Job ID from list */
1316 http_t *http; /* HTTP server connection */
1317 ipp_t *request, /* IPP Request */
1318 *response; /* IPP Response */
1319 ipp_attribute_t *attr; /* Current attribute */
1320 ipp_pstate_t state; /* Printer state */
1321 const char *jobdest, /* Pointer into job-printer-uri */
1322 *jobuser, /* Pointer to job-originating-user-name */
1323 *jobname; /* Pointer to job-name */
1324 ipp_jstate_t jobstate; /* job-state */
1325 int jobid, /* job-id */
1326 jobsize, /* job-k-octets */
1327 jobcount, /* Number of jobs */
1328 jobcopies, /* Number of copies */
1329 rank; /* Rank of job */
1330 char rankstr[255]; /* Rank string */
1331 char namestr[1024]; /* Job name string */
1332 char uri[HTTP_MAX_URI]; /* Printer URI */
1333 char dest[256]; /* Printer/class queue */
1334 static const char * const ranks[10] = /* Ranking strings */
1347 static const char * const requested[] =
1348 { /* Requested attributes */
1353 "job-originating-user-name",
1360 * Try connecting to the local server...
1363 if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
1364 cupsEncryption())) == NULL)
1366 syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(),
1368 printf("Unable to connect to server %s: %s", cupsServer(), strerror(errno));
1373 * Get the actual destination name and printer state...
1376 if (get_printer(http, queue, dest, sizeof(dest), NULL, NULL, NULL, &state))
1378 syslog(LOG_ERR, "Unable to get printer %s: %s", queue,
1379 cupsLastErrorString());
1380 printf("Unable to get printer %s: %s", queue, cupsLastErrorString());
1385 * Show the queue state...
1390 case IPP_PRINTER_IDLE :
1391 printf("%s is ready\n", dest);
1393 case IPP_PRINTER_PROCESSING :
1394 printf("%s is ready and printing\n", dest);
1396 case IPP_PRINTER_STOPPED :
1397 printf("%s is not ready\n", dest);
1402 * Build an IPP_GET_JOBS or IPP_GET_JOB_ATTRIBUTES request, which requires
1403 * the following attributes:
1405 * attributes-charset
1406 * attributes-natural-language
1407 * job-uri or printer-uri
1412 request = ippNewRequest(id ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS);
1414 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
1415 "localhost", 0, "/printers/%s", dest);
1417 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1421 ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id);
1424 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
1425 "requesting-user-name", NULL, list);
1426 ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
1429 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1430 "requested-attributes",
1431 sizeof(requested) / sizeof(requested[0]),
1435 * Do the request and get back a response...
1439 response = cupsDoRequest(http, request, "/");
1441 if (cupsLastError() > IPP_OK_CONFLICT)
1443 printf("get-jobs failed: %s\n", cupsLastErrorString());
1444 ippDelete(response);
1449 * Loop through the job list and display them...
1452 for (attr = response->attrs, rank = 1; attr; attr = attr->next)
1455 * Skip leading attributes until we hit a job...
1458 while (attr && (attr->group_tag != IPP_TAG_JOB || !attr->name))
1465 * Pull the needed attributes from this job...
1470 jobstate = IPP_JOB_PENDING;
1471 jobname = "untitled";
1476 while (attr && attr->group_tag == IPP_TAG_JOB)
1478 if (!strcmp(attr->name, "job-id") &&
1479 attr->value_tag == IPP_TAG_INTEGER)
1480 jobid = attr->values[0].integer;
1482 if (!strcmp(attr->name, "job-k-octets") &&
1483 attr->value_tag == IPP_TAG_INTEGER)
1484 jobsize = attr->values[0].integer;
1486 if (!strcmp(attr->name, "job-state") &&
1487 attr->value_tag == IPP_TAG_ENUM)
1488 jobstate = (ipp_jstate_t)attr->values[0].integer;
1490 if (!strcmp(attr->name, "job-printer-uri") &&
1491 attr->value_tag == IPP_TAG_URI)
1492 if ((jobdest = strrchr(attr->values[0].string.text, '/')) != NULL)
1495 if (!strcmp(attr->name, "job-originating-user-name") &&
1496 attr->value_tag == IPP_TAG_NAME)
1497 jobuser = attr->values[0].string.text;
1499 if (!strcmp(attr->name, "job-name") &&
1500 attr->value_tag == IPP_TAG_NAME)
1501 jobname = attr->values[0].string.text;
1503 if (!strcmp(attr->name, "copies") &&
1504 attr->value_tag == IPP_TAG_INTEGER)
1505 jobcopies = attr->values[0].integer;
1511 * See if we have everything needed...
1514 if (!jobdest || !jobid)
1522 if (!longstatus && jobcount == 0)
1523 puts("Rank Owner Job File(s) Total Size");
1528 * Display the job...
1531 if (jobstate == IPP_JOB_PROCESSING)
1532 strcpy(rankstr, "active");
1535 snprintf(rankstr, sizeof(rankstr), "%d%s", rank, ranks[rank % 10]);
1544 snprintf(namestr, sizeof(namestr), "%d copies of %s", jobcopies,
1547 strlcpy(namestr, jobname, sizeof(namestr));
1549 printf("%s: %-33.33s [job %d localhost]\n", jobuser, rankstr, jobid);
1550 printf(" %-39.39s %.0f bytes\n", namestr, 1024.0 * jobsize);
1553 printf("%-7s %-7.7s %-7d %-31.31s %.0f bytes\n", rankstr, jobuser,
1554 jobid, jobname, 1024.0 * jobsize);
1560 ippDelete(response);
1572 * 'smart_gets()' - Get a line of text, removing the trailing CR and/or LF.
1575 static char * /* O - Line read or NULL */
1576 smart_gets(char *s, /* I - Pointer to line buffer */
1577 int len, /* I - Size of line buffer */
1578 FILE *fp) /* I - File to read from */
1580 char *ptr, /* Pointer into line */
1581 *end; /* End of line */
1582 int ch; /* Character from file */
1586 * Read the line; unlike fgets(), we read the entire line but dump
1587 * characters that go past the end of the buffer. Also, we accept
1588 * CR, LF, or CR LF for the line endings to be "safe", although
1589 * RFC 1179 specifically says "just use LF".
1595 while ((ch = getc(fp)) != EOF)
1599 else if (ch == '\r')
1602 * See if a LF follows...
1618 if (ch == EOF && ptr == s)
1626 * End of "$Id: cups-lpd.c 10379 2012-03-23 22:16:22Z mike $".