2 * Line Printer Daemon interface for CUPS.
4 * Copyright 2007-2016 by Apple Inc.
5 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file. If this file is
11 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * Include necessary headers...
18 #define _CUPS_NO_DEPRECATED
19 #include <cups/cups-private.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
28 #ifdef HAVE_INTTYPES_H
29 # include <inttypes.h>
30 #endif /* HAVE_INTTYPES_H */
33 #endif /* __APPLE__ */
37 * LPD "mini-daemon" for CUPS. This program must be used in conjunction
38 * with inetd or another similar program that monitors ports and starts
39 * daemons for each client connection. A typical configuration is:
41 * printer stream tcp nowait lp /usr/lib/cups/daemon/cups-lpd cups-lpd
43 * This daemon implements most of RFC 1179 (the unofficial LPD specification)
46 * - This daemon does not check to make sure that the source port is
47 * between 721 and 731, since it isn't necessary for proper
48 * functioning and port-based security is no security at all!
50 * - The "Print any waiting jobs" command is a no-op.
52 * The LPD-to-IPP mapping is as defined in RFC 2569. The report formats
53 * currently match the Solaris LPD mini-daemon.
60 static int create_job(http_t
*http
, const char *dest
, const char *title
, const char *user
, int num_options
, cups_option_t
*options
);
61 static int get_printer(http_t
*http
, const char *name
, char *dest
,
62 size_t destsize
, cups_option_t
**options
,
63 int *accepting
, int *shared
, ipp_pstate_t
*state
);
64 static int print_file(http_t
*http
, int id
, const char *filename
,
65 const char *docname
, const char *user
,
66 const char *format
, int last
);
67 static int recv_print_job(const char *name
, int num_defaults
,
68 cups_option_t
*defaults
);
69 static int remove_jobs(const char *name
, const char *agent
,
71 static int send_state(const char *name
, const char *list
,
73 static char *smart_gets(char *s
, int len
, FILE *fp
);
74 static void smart_strlcpy(char *dst
, const char *src
, size_t dstsize
);
78 * 'main()' - Process an incoming LPD request...
81 int /* O - Exit status */
82 main(int argc
, /* I - Number of command-line arguments */
83 char *argv
[]) /* I - Command-line arguments */
85 int i
; /* Looping var */
86 int num_defaults
; /* Number of default options */
87 cups_option_t
*defaults
; /* Default options */
88 char line
[256], /* Command string */
89 command
, /* Command code */
90 *dest
, /* Pointer to destination */
91 *list
, /* Pointer to list */
92 *agent
, /* Pointer to user */
93 status
; /* Status for client */
94 socklen_t hostlen
; /* Size of client address */
95 http_addr_t hostaddr
; /* Address of client */
96 char hostname
[256], /* Name of client */
97 hostip
[256], /* IP address */
98 *hostfamily
; /* Address family */
99 int hostlookups
; /* Do hostname lookups? */
101 vproc_transaction_t vtran
= vproc_transaction_begin(NULL
);
102 #endif /* __APPLE__ */
106 * Don't buffer the output...
109 setbuf(stdout
, NULL
);
112 * Log things using the "cups-lpd" name...
115 openlog("cups-lpd", LOG_PID
, LOG_LPR
);
118 * Scan the command-line for options...
125 for (i
= 1; i
< argc
; i
++)
126 if (argv
[i
][0] == '-')
130 case 'h' : /* -h hostname[:port] */
132 cupsSetServer(argv
[i
] + 2);
137 cupsSetServer(argv
[i
]);
139 syslog(LOG_WARNING
, "Expected hostname string after -h option!");
143 case 'o' : /* Option */
145 num_defaults
= cupsParseOptions(argv
[i
] + 2, num_defaults
,
151 num_defaults
= cupsParseOptions(argv
[i
], num_defaults
,
154 syslog(LOG_WARNING
, "Expected option string after -o option!");
158 case 'n' : /* Don't do hostname lookups */
163 syslog(LOG_WARNING
, "Unknown option \"%c\" ignored!", argv
[i
][1]);
168 syslog(LOG_WARNING
, "Unknown command-line option \"%s\" ignored!",
172 * Get the address of the client...
175 hostlen
= sizeof(hostaddr
);
177 if (getpeername(0, (struct sockaddr
*)&hostaddr
, &hostlen
))
179 syslog(LOG_WARNING
, "Unable to get client address - %s", strerror(errno
));
180 strlcpy(hostname
, "unknown", sizeof(hostname
));
184 httpAddrString(&hostaddr
, hostip
, sizeof(hostip
));
187 httpAddrLookup(&hostaddr
, hostname
, sizeof(hostname
));
189 strlcpy(hostname
, hostip
, sizeof(hostname
));
192 if (hostaddr
.addr
.sa_family
== AF_INET6
)
195 #endif /* AF_INET6 */
198 syslog(LOG_INFO
, "Connection from %s (%s %s)", hostname
, hostfamily
,
202 num_defaults
= cupsAddOption("job-originating-host-name", hostname
,
203 num_defaults
, &defaults
);
206 * RFC1179 specifies that only 1 daemon command can be received for
210 if (smart_gets(line
, sizeof(line
), stdin
) == NULL
)
213 * Unable to get command from client! Send an error status and return.
216 syslog(LOG_ERR
, "Unable to get command line from client!");
220 vproc_transaction_end(NULL
, vtran
);
221 #endif /* __APPLE__ */
227 * The first byte is the command byte. After that will be the queue name,
228 * resource list, and/or user name.
231 if ((command
= line
[0]) == '\0')
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
= (char)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
= (char)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
= (char)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
= (char)remove_jobs(dest
, agent
, list
);
312 syslog(LOG_INFO
, "Closing connection");
316 vproc_transaction_end(NULL
, vtran
);
317 #endif /* __APPLE__ */
324 * 'create_job()' - Create a new print job.
327 static int /* O - Job ID or -1 on error */
328 create_job(http_t
*http
, /* I - HTTP connection */
329 const char *dest
, /* I - Destination name */
330 const char *title
, /* I - job-name */
331 const char *user
, /* I - requesting-user-name */
332 int num_options
, /* I - Number of options for job */
333 cups_option_t
*options
) /* I - Options for job */
335 ipp_t
*request
; /* IPP request */
336 ipp_t
*response
; /* IPP response */
337 ipp_attribute_t
*attr
; /* IPP attribute */
338 char uri
[HTTP_MAX_URI
]; /* Printer URI */
343 * Setup the Create-Job request...
346 request
= ippNewRequest(IPP_OP_CREATE_JOB
);
348 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
349 "localhost", 0, "/printers/%s", dest
);
351 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
354 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
355 "requesting-user-name", NULL
, user
);
358 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name",
361 cupsEncodeOptions(request
, num_options
, options
);
367 snprintf(uri
, sizeof(uri
), "/printers/%s", dest
);
369 response
= cupsDoRequest(http
, request
, uri
);
371 if (!response
|| cupsLastError() > IPP_STATUS_OK_CONFLICTING
)
373 syslog(LOG_ERR
, "Unable to create job - %s", cupsLastErrorString());
381 * Get the job-id value from the response and return it...
384 if ((attr
= ippFindAttribute(response
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
388 syslog(LOG_ERR
, "No job-id attribute found in response from server!");
392 id
= attr
->values
[0].integer
;
394 syslog(LOG_INFO
, "Print file - job ID = %d", id
);
404 * 'get_printer()' - Get the named printer and its options.
407 static int /* O - Number of options or -1 on error */
408 get_printer(http_t
*http
, /* I - HTTP connection */
409 const char *name
, /* I - Printer name from request */
410 char *dest
, /* I - Destination buffer */
411 size_t destsize
, /* I - Size of destination buffer */
412 cups_option_t
**options
, /* O - Printer options */
413 int *accepting
, /* O - printer-is-accepting-jobs value */
414 int *shared
, /* O - printer-is-shared value */
415 ipp_pstate_t
*state
) /* O - printer-state value */
417 int num_options
; /* Number of options */
418 cups_file_t
*fp
; /* lpoptions file */
419 char line
[1024], /* Line from lpoptions file */
420 *value
, /* Pointer to value on line */
421 *optptr
; /* Pointer to options on line */
422 int linenum
; /* Line number in file */
423 const char *cups_serverroot
; /* CUPS_SERVERROOT env var */
424 ipp_t
*request
; /* IPP request */
425 ipp_t
*response
; /* IPP response */
426 ipp_attribute_t
*attr
; /* IPP attribute */
427 char uri
[HTTP_MAX_URI
]; /* Printer URI */
428 static const char * const requested
[] =
429 { /* Requested attributes */
431 "printer-is-accepting-jobs",
439 * Initialize everything...
447 *state
= IPP_PSTATE_STOPPED
;
452 * See if the name is a queue name optionally with an instance name.
455 strlcpy(dest
, name
, destsize
);
456 if ((value
= strchr(dest
, '/')) != NULL
)
460 * Setup the Get-Printer-Attributes request...
463 request
= ippNewRequest(IPP_OP_GET_PRINTER_ATTRIBUTES
);
465 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
466 "localhost", 0, "/printers/%s", dest
);
468 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
471 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
472 "requested-attributes",
473 (int)(sizeof(requested
) / sizeof(requested
[0])),
480 response
= cupsDoRequest(http
, request
, "/");
482 if (!response
|| cupsLastError() > IPP_STATUS_OK_CONFLICTING
)
485 * If we can't find the printer by name, look up the printer-name
486 * using the printer-info values...
489 ipp_attribute_t
*accepting_attr
,/* printer-is-accepting-jobs */
490 *info_attr
, /* printer-info */
491 *name_attr
, /* printer-name */
492 *shared_attr
, /* printer-is-shared */
493 *state_attr
; /* printer-state */
499 * Setup the CUPS-Get-Printers request...
502 request
= ippNewRequest(IPP_OP_CUPS_GET_PRINTERS
);
504 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
505 "requested-attributes",
506 (int)(sizeof(requested
) / sizeof(requested
[0])),
513 response
= cupsDoRequest(http
, request
, "/");
515 if (!response
|| cupsLastError() > IPP_STATUS_OK_CONFLICTING
)
517 syslog(LOG_ERR
, "Unable to get list of printers - %s",
518 cupsLastErrorString());
526 * Scan the response for printers...
530 attr
= response
->attrs
;
535 * Skip to the next printer...
538 while (attr
&& attr
->group_tag
!= IPP_TAG_PRINTER
)
545 * Get all of the attributes for the current printer...
548 accepting_attr
= NULL
;
554 while (attr
&& attr
->group_tag
== IPP_TAG_PRINTER
)
556 if (!strcmp(attr
->name
, "printer-is-accepting-jobs") &&
557 attr
->value_tag
== IPP_TAG_BOOLEAN
)
558 accepting_attr
= attr
;
559 else if (!strcmp(attr
->name
, "printer-info") &&
560 attr
->value_tag
== IPP_TAG_TEXT
)
562 else if (!strcmp(attr
->name
, "printer-name") &&
563 attr
->value_tag
== IPP_TAG_NAME
)
565 else if (!strcmp(attr
->name
, "printer-is-shared") &&
566 attr
->value_tag
== IPP_TAG_BOOLEAN
)
568 else if (!strcmp(attr
->name
, "printer-state") &&
569 attr
->value_tag
== IPP_TAG_ENUM
)
575 if (info_attr
&& name_attr
&&
576 !_cups_strcasecmp(name
, info_attr
->values
[0].string
.text
))
579 * Found a match, use this one!
582 strlcpy(dest
, name_attr
->values
[0].string
.text
, destsize
);
584 if (accepting
&& accepting_attr
)
585 *accepting
= accepting_attr
->values
[0].boolean
;
587 if (shared
&& shared_attr
)
588 *shared
= shared_attr
->values
[0].boolean
;
590 if (state
&& state_attr
)
591 *state
= (ipp_pstate_t
)state_attr
->values
[0].integer
;
601 syslog(LOG_ERR
, "Unable to find \"%s\" in list of printers!", name
);
611 * Get values from the response...
616 if ((attr
= ippFindAttribute(response
, "printer-is-accepting-jobs",
617 IPP_TAG_BOOLEAN
)) == NULL
)
618 syslog(LOG_ERR
, "No printer-is-accepting-jobs attribute found in "
619 "response from server!");
621 *accepting
= attr
->values
[0].boolean
;
626 if ((attr
= ippFindAttribute(response
, "printer-is-shared",
627 IPP_TAG_BOOLEAN
)) == NULL
)
629 syslog(LOG_ERR
, "No printer-is-shared attribute found in "
630 "response from server!");
634 *shared
= attr
->values
[0].boolean
;
639 if ((attr
= ippFindAttribute(response
, "printer-state",
640 IPP_TAG_ENUM
)) == NULL
)
641 syslog(LOG_ERR
, "No printer-state attribute found in "
642 "response from server!");
644 *state
= (ipp_pstate_t
)attr
->values
[0].integer
;
651 * Next look for the printer in the lpoptions file...
656 if (options
&& shared
&& accepting
)
658 if ((cups_serverroot
= getenv("CUPS_SERVERROOT")) == NULL
)
659 cups_serverroot
= CUPS_SERVERROOT
;
661 snprintf(line
, sizeof(line
), "%s/lpoptions", cups_serverroot
);
662 if ((fp
= cupsFileOpen(line
, "r")) != NULL
)
665 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
668 * Make sure we have "Dest name options" or "Default name options"...
671 if ((_cups_strcasecmp(line
, "Dest") && _cups_strcasecmp(line
, "Default")) || !value
)
675 * Separate destination name from options...
678 for (optptr
= value
; *optptr
&& !isspace(*optptr
& 255); optptr
++);
680 while (*optptr
== ' ')
684 * If this is our destination, parse the options and break out of
685 * the loop - we're done!
688 if (!_cups_strcasecmp(value
, name
))
690 num_options
= cupsParseOptions(optptr
, num_options
, options
);
702 * Return the number of options for this destination...
705 return (num_options
);
710 * 'print_file()' - Add a file to the current job.
713 static int /* O - 0 on success, -1 on failure */
714 print_file(http_t
*http
, /* I - HTTP connection */
715 int id
, /* I - Job ID */
716 const char *filename
, /* I - File to print */
717 const char *docname
, /* I - document-name */
718 const char *user
, /* I - requesting-user-name */
719 const char *format
, /* I - document-format */
720 int last
) /* I - 1 = last file in job */
722 ipp_t
*request
; /* IPP request */
723 char uri
[HTTP_MAX_URI
]; /* Printer URI */
727 * Setup the Send-Document request...
730 request
= ippNewRequest(IPP_OP_SEND_DOCUMENT
);
732 snprintf(uri
, sizeof(uri
), "ipp://localhost/jobs/%d", id
);
733 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
735 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
736 "requesting-user-name", NULL
, user
);
739 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
740 "document-name", NULL
, docname
);
743 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_MIMETYPE
,
744 "document-format", NULL
, format
);
746 ippAddBoolean(request
, IPP_TAG_OPERATION
, "last-document", (char)last
);
752 snprintf(uri
, sizeof(uri
), "/jobs/%d", id
);
754 ippDelete(cupsDoFileRequest(http
, request
, uri
, filename
));
756 if (cupsLastError() > IPP_STATUS_OK_CONFLICTING
)
758 syslog(LOG_ERR
, "Unable to send document - %s", cupsLastErrorString());
768 * 'recv_print_job()' - Receive a print job from the client.
771 static int /* O - Command status */
773 const char *queue
, /* I - Printer name */
774 int num_defaults
, /* I - Number of default options */
775 cups_option_t
*defaults
) /* I - Default options */
777 http_t
*http
; /* HTTP connection */
778 int i
; /* Looping var */
779 int status
; /* Command status */
780 int fd
; /* Temporary file */
781 FILE *fp
; /* File pointer */
782 char filename
[1024]; /* Temporary filename */
783 ssize_t bytes
; /* Bytes received */
784 size_t total
; /* Total bytes */
785 char line
[256], /* Line from file/stdin */
786 command
, /* Command from line */
787 *count
, /* Number of bytes */
788 *name
; /* Name of file */
789 const char *job_sheets
; /* Job sheets */
790 int num_data
; /* Number of data files */
791 char control
[1024], /* Control filename */
792 data
[100][256], /* Data files */
793 temp
[100][1024]; /* Temporary files */
794 char user
[1024], /* User name */
795 title
[1024], /* Job title */
796 docname
[1024], /* Document name */
797 dest
[256]; /* Printer/class queue */
798 int accepting
, /* printer-is-accepting */
799 shared
, /* printer-is-shared */
800 num_options
; /* Number of options */
801 cups_option_t
*options
; /* Options */
803 int docnumber
, /* Current document number */
804 doccount
; /* Count of documents */
808 * Connect to the server...
811 http
= httpConnect2(cupsServer(), ippPort(), NULL
, AF_UNSPEC
, cupsEncryption(), 1, 30000, NULL
);
814 syslog(LOG_ERR
, "Unable to connect to server: %s", strerror(errno
));
822 * See if the printer is available...
825 num_options
= get_printer(http
, queue
, dest
, sizeof(dest
), &options
,
826 &accepting
, &shared
, NULL
);
828 if (num_options
< 0 || !accepting
|| !shared
)
831 syslog(LOG_INFO
, "Rejecting job because \"%s\" is not %s", dest
,
832 !accepting
? "accepting jobs" : "shared");
834 syslog(LOG_ERR
, "Unable to get printer information for \"%s\"", queue
);
843 putchar(0); /* OK so far... */
846 * Read the request...
855 while (smart_gets(line
, sizeof(line
), stdin
) != NULL
)
857 if (strlen(line
) < 2)
866 for (name
= count
+ 1; *name
&& !isspace(*name
& 255); name
++);
867 while (isspace(*name
& 255))
873 case 0x01 : /* Abort */
877 case 0x02 : /* Receive control file */
878 if (strlen(name
) < 2)
880 syslog(LOG_ERR
, "Bad control file name \"%s\"", name
);
889 * Append to the existing control file - the LPD spec is
890 * not entirely clear, but at least the OS/2 LPD code sends
891 * multiple control files per connection...
894 if ((fd
= open(control
, O_WRONLY
)) < 0)
897 "Unable to append to temporary control file \"%s\" - %s",
898 control
, strerror(errno
));
904 lseek(fd
, 0, SEEK_END
);
908 if ((fd
= cupsTempFd(control
, sizeof(control
))) < 0)
910 syslog(LOG_ERR
, "Unable to open temporary control file \"%s\" - %s",
911 control
, strerror(errno
));
917 strlcpy(filename
, control
, sizeof(filename
));
921 case 0x03 : /* Receive data file */
922 if (strlen(name
) < 2)
924 syslog(LOG_ERR
, "Bad data file name \"%s\"", name
);
930 if (num_data
>= (int)(sizeof(data
) / sizeof(data
[0])))
933 * Too many data files...
936 syslog(LOG_ERR
, "Too many data files (%d)", num_data
);
942 strlcpy(data
[num_data
], name
, sizeof(data
[0]));
944 if ((fd
= cupsTempFd(temp
[num_data
], sizeof(temp
[0]))) < 0)
946 syslog(LOG_ERR
, "Unable to open temporary data file \"%s\" - %s",
947 temp
[num_data
], strerror(errno
));
953 strlcpy(filename
, temp
[num_data
], sizeof(filename
));
965 * Copy the data or control file from the client...
968 for (total
= (size_t)strtoll(count
, NULL
, 10); total
> 0; total
-= (size_t)bytes
)
970 if (total
> sizeof(line
))
971 bytes
= (ssize_t
)sizeof(line
);
973 bytes
= (ssize_t
)total
;
975 if ((bytes
= (ssize_t
)fread(line
, 1, (size_t)bytes
, stdin
)) > 0)
976 bytes
= write(fd
, line
, (size_t)bytes
);
980 syslog(LOG_ERR
, "Error while reading file - %s",
988 * Read trailing nul...
993 if (fread(line
, 1, 1, stdin
) < 1)
996 syslog(LOG_ERR
, "Error while reading trailing nul - %s",
1002 syslog(LOG_ERR
, "Trailing character after file is not nul (%02X)!",
1008 * Close the file and send an acknowledgement...
1022 * Process the control file and print stuff...
1025 if ((fp
= fopen(control
, "rb")) == NULL
)
1030 * Copy the default options...
1033 for (i
= 0; i
< num_defaults
; i
++)
1034 num_options
= cupsAddOption(defaults
[i
].name
,
1036 num_options
, &options
);
1039 * Grab the job information...
1047 while (smart_gets(line
, sizeof(line
), fp
) != NULL
)
1050 * Process control lines...
1055 case 'J' : /* Job name */
1056 smart_strlcpy(title
, line
+ 1, sizeof(title
));
1059 case 'N' : /* Document name */
1060 smart_strlcpy(docname
, line
+ 1, sizeof(docname
));
1063 case 'P' : /* User identification */
1064 smart_strlcpy(user
, line
+ 1, sizeof(user
));
1067 case 'L' : /* Print banner page */
1069 * If a banner was requested and it's not overridden by a
1070 * command line option and the destination's default is none
1071 * then add the standard banner...
1074 if (cupsGetOption("job-sheets", num_defaults
, defaults
) == NULL
&&
1075 ((job_sheets
= cupsGetOption("job-sheets", num_options
,
1076 options
)) == NULL
||
1077 !strcmp(job_sheets
, "none,none")))
1079 num_options
= cupsAddOption("job-sheets", "standard",
1080 num_options
, &options
);
1084 case 'c' : /* Plot CIF file */
1085 case 'd' : /* Print DVI file */
1086 case 'f' : /* Print formatted file */
1087 case 'g' : /* Plot file */
1088 case 'l' : /* Print file leaving control characters (raw) */
1089 case 'n' : /* Print ditroff output file */
1090 case 'o' : /* Print PostScript output file */
1091 case 'p' : /* Print file with 'pr' format (prettyprint) */
1092 case 'r' : /* File to print with FORTRAN carriage control */
1093 case 't' : /* Print troff output file */
1094 case 'v' : /* Print raster file */
1097 if (line
[0] == 'l' &&
1098 !cupsGetOption("document-format", num_options
, options
))
1099 num_options
= cupsAddOption("raw", "", num_options
, &options
);
1102 num_options
= cupsAddOption("prettyprint", "", num_options
,
1112 * Check that we have a username...
1117 syslog(LOG_WARNING
, "No username specified by client! "
1118 "Using \"anonymous\"...");
1119 strlcpy(user
, "anonymous", sizeof(user
));
1126 if ((id
= create_job(http
, dest
, title
, user
, num_options
, options
)) < 0)
1131 * Then print the job files...
1139 while (smart_gets(line
, sizeof(line
), fp
) != NULL
)
1142 * Process control lines...
1147 case 'N' : /* Document name */
1148 smart_strlcpy(docname
, line
+ 1, sizeof(docname
));
1151 case 'c' : /* Plot CIF file */
1152 case 'd' : /* Print DVI file */
1153 case 'f' : /* Print formatted file */
1154 case 'g' : /* Plot file */
1155 case 'l' : /* Print file leaving control characters (raw) */
1156 case 'n' : /* Print ditroff output file */
1157 case 'o' : /* Print PostScript output file */
1158 case 'p' : /* Print file with 'pr' format (prettyprint) */
1159 case 'r' : /* File to print with FORTRAN carriage control */
1160 case 't' : /* Print troff output file */
1161 case 'v' : /* Print raster file */
1163 * Figure out which file we are printing...
1166 for (i
= 0; i
< num_data
; i
++)
1167 if (!strcmp(data
[i
], line
+ 1))
1177 * Send the print file...
1182 if (print_file(http
, id
, temp
[i
], docname
, user
,
1183 cupsGetOption("document-format", num_options
,
1185 docnumber
== doccount
))
1202 cupsFreeOptions(num_options
, options
);
1207 * Clean up all temporary files and return...
1212 for (i
= 0; i
< num_data
; i
++)
1220 * 'remove_jobs()' - Cancel one or more jobs.
1223 static int /* O - Command status */
1224 remove_jobs(const char *dest
, /* I - Destination */
1225 const char *agent
, /* I - User agent */
1226 const char *list
) /* I - List of jobs or users */
1228 int id
; /* Job ID */
1229 http_t
*http
; /* HTTP server connection */
1230 ipp_t
*request
; /* IPP Request */
1231 char uri
[HTTP_MAX_URI
]; /* Job URI */
1234 (void)dest
; /* Suppress compiler warnings... */
1237 * Try connecting to the local server...
1240 if ((http
= httpConnect2(cupsServer(), ippPort(), NULL
, AF_UNSPEC
, cupsEncryption(), 1, 30000, NULL
)) == NULL
)
1242 syslog(LOG_ERR
, "Unable to connect to server %s: %s", cupsServer(),
1248 * Loop for each job...
1251 while ((id
= atoi(list
)) > 0)
1254 * Skip job ID in list...
1257 while (isdigit(*list
& 255))
1259 while (isspace(*list
& 255))
1263 * Build an IPP_OP_CANCEL_JOB request, which requires the following
1266 * attributes-charset
1267 * attributes-natural-language
1269 * requesting-user-name
1272 request
= ippNewRequest(IPP_OP_CANCEL_JOB
);
1274 sprintf(uri
, "ipp://localhost/jobs/%d", id
);
1275 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1277 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1278 "requesting-user-name", NULL
, agent
);
1281 * Do the request and get back a response...
1284 ippDelete(cupsDoRequest(http
, request
, "/jobs"));
1286 if (cupsLastError() > IPP_STATUS_OK_CONFLICTING
)
1288 syslog(LOG_WARNING
, "Cancel of job ID %d failed: %s\n", id
,
1289 cupsLastErrorString());
1294 syslog(LOG_INFO
, "Job ID %d canceled", id
);
1304 * 'send_state()' - Send the queue state.
1307 static int /* O - Command status */
1308 send_state(const char *queue
, /* I - Destination */
1309 const char *list
, /* I - Job or user */
1310 int longstatus
) /* I - List of jobs or users */
1312 int id
; /* Job ID from list */
1313 http_t
*http
; /* HTTP server connection */
1314 ipp_t
*request
, /* IPP Request */
1315 *response
; /* IPP Response */
1316 ipp_attribute_t
*attr
; /* Current attribute */
1317 ipp_pstate_t state
; /* Printer state */
1318 const char *jobdest
, /* Pointer into job-printer-uri */
1319 *jobuser
, /* Pointer to job-originating-user-name */
1320 *jobname
; /* Pointer to job-name */
1321 ipp_jstate_t jobstate
; /* job-state */
1322 int jobid
, /* job-id */
1323 jobsize
, /* job-k-octets */
1324 jobcount
, /* Number of jobs */
1325 jobcopies
, /* Number of copies */
1326 rank
; /* Rank of job */
1327 char rankstr
[255]; /* Rank string */
1328 char namestr
[1024]; /* Job name string */
1329 char uri
[HTTP_MAX_URI
]; /* Printer URI */
1330 char dest
[256]; /* Printer/class queue */
1331 static const char * const ranks
[10] = /* Ranking strings */
1344 static const char * const requested
[] =
1345 { /* Requested attributes */
1350 "job-originating-user-name",
1357 * Try connecting to the local server...
1360 if ((http
= httpConnect2(cupsServer(), ippPort(), NULL
, AF_UNSPEC
, cupsEncryption(), 1, 30000, NULL
)) == NULL
)
1362 syslog(LOG_ERR
, "Unable to connect to server %s: %s", cupsServer(),
1364 printf("Unable to connect to server %s: %s", cupsServer(), strerror(errno
));
1369 * Get the actual destination name and printer state...
1372 if (get_printer(http
, queue
, dest
, sizeof(dest
), NULL
, NULL
, NULL
, &state
))
1374 syslog(LOG_ERR
, "Unable to get printer %s: %s", queue
,
1375 cupsLastErrorString());
1376 printf("Unable to get printer %s: %s", queue
, cupsLastErrorString());
1381 * Show the queue state...
1386 case IPP_PSTATE_IDLE
:
1387 printf("%s is ready\n", dest
);
1389 case IPP_PSTATE_PROCESSING
:
1390 printf("%s is ready and printing\n", dest
);
1392 case IPP_PSTATE_STOPPED
:
1393 printf("%s is not ready\n", dest
);
1398 * Build an IPP_OP_GET_JOBS or IPP_OP_GET_JOB_ATTRIBUTES request, which requires
1399 * the following attributes:
1401 * attributes-charset
1402 * attributes-natural-language
1403 * job-uri or printer-uri
1408 request
= ippNewRequest(id
? IPP_OP_GET_JOB_ATTRIBUTES
: IPP_OP_GET_JOBS
);
1410 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1411 "localhost", 0, "/printers/%s", dest
);
1413 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1417 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", id
);
1420 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1421 "requesting-user-name", NULL
, list
);
1422 ippAddBoolean(request
, IPP_TAG_OPERATION
, "my-jobs", 1);
1425 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1426 "requested-attributes",
1427 sizeof(requested
) / sizeof(requested
[0]),
1431 * Do the request and get back a response...
1435 response
= cupsDoRequest(http
, request
, "/");
1437 if (cupsLastError() > IPP_STATUS_OK_CONFLICTING
)
1439 printf("get-jobs failed: %s\n", cupsLastErrorString());
1440 ippDelete(response
);
1445 * Loop through the job list and display them...
1448 for (attr
= response
->attrs
, rank
= 1; attr
; attr
= attr
->next
)
1451 * Skip leading attributes until we hit a job...
1454 while (attr
&& (attr
->group_tag
!= IPP_TAG_JOB
|| !attr
->name
))
1461 * Pull the needed attributes from this job...
1466 jobstate
= IPP_JSTATE_PENDING
;
1467 jobname
= "untitled";
1472 while (attr
&& attr
->group_tag
== IPP_TAG_JOB
)
1474 if (!strcmp(attr
->name
, "job-id") &&
1475 attr
->value_tag
== IPP_TAG_INTEGER
)
1476 jobid
= attr
->values
[0].integer
;
1478 if (!strcmp(attr
->name
, "job-k-octets") &&
1479 attr
->value_tag
== IPP_TAG_INTEGER
)
1480 jobsize
= attr
->values
[0].integer
;
1482 if (!strcmp(attr
->name
, "job-state") &&
1483 attr
->value_tag
== IPP_TAG_ENUM
)
1484 jobstate
= (ipp_jstate_t
)attr
->values
[0].integer
;
1486 if (!strcmp(attr
->name
, "job-printer-uri") &&
1487 attr
->value_tag
== IPP_TAG_URI
)
1488 if ((jobdest
= strrchr(attr
->values
[0].string
.text
, '/')) != NULL
)
1491 if (!strcmp(attr
->name
, "job-originating-user-name") &&
1492 attr
->value_tag
== IPP_TAG_NAME
)
1493 jobuser
= attr
->values
[0].string
.text
;
1495 if (!strcmp(attr
->name
, "job-name") &&
1496 attr
->value_tag
== IPP_TAG_NAME
)
1497 jobname
= attr
->values
[0].string
.text
;
1499 if (!strcmp(attr
->name
, "copies") &&
1500 attr
->value_tag
== IPP_TAG_INTEGER
)
1501 jobcopies
= attr
->values
[0].integer
;
1507 * See if we have everything needed...
1510 if (!jobdest
|| !jobid
)
1518 if (!longstatus
&& jobcount
== 0)
1519 puts("Rank Owner Job File(s) Total Size");
1524 * Display the job...
1527 if (jobstate
== IPP_JSTATE_PROCESSING
)
1528 strlcpy(rankstr
, "active", sizeof(rankstr
));
1531 snprintf(rankstr
, sizeof(rankstr
), "%d%s", rank
, ranks
[rank
% 10]);
1540 snprintf(namestr
, sizeof(namestr
), "%d copies of %s", jobcopies
,
1543 strlcpy(namestr
, jobname
, sizeof(namestr
));
1545 printf("%s: %-33.33s [job %d localhost]\n", jobuser
, rankstr
, jobid
);
1546 printf(" %-39.39s %.0f bytes\n", namestr
, 1024.0 * jobsize
);
1549 printf("%-7s %-7.7s %-7d %-31.31s %.0f bytes\n", rankstr
, jobuser
,
1550 jobid
, jobname
, 1024.0 * jobsize
);
1556 ippDelete(response
);
1568 * 'smart_gets()' - Get a line of text, removing the trailing CR and/or LF.
1571 static char * /* O - Line read or NULL */
1572 smart_gets(char *s
, /* I - Pointer to line buffer */
1573 int len
, /* I - Size of line buffer */
1574 FILE *fp
) /* I - File to read from */
1576 char *ptr
, /* Pointer into line */
1577 *end
; /* End of line */
1578 int ch
; /* Character from file */
1582 * Read the line; unlike fgets(), we read the entire line but dump
1583 * characters that go past the end of the buffer. Also, we accept
1584 * CR, LF, or CR LF for the line endings to be "safe", although
1585 * RFC 1179 specifically says "just use LF".
1591 while ((ch
= getc(fp
)) != EOF
)
1595 else if (ch
== '\r')
1598 * See if a LF follows...
1614 if (ch
== EOF
&& ptr
== s
)
1622 * 'smart_strlcpy()' - Copy a string and convert from ISO-8859-1 to UTF-8 as needed.
1626 smart_strlcpy(char *dst
, /* I - Output buffer */
1627 const char *src
, /* I - Input string */
1628 size_t dstsize
) /* I - Size of output buffer */
1630 const unsigned char *srcptr
; /* Pointer into input string */
1631 unsigned char *dstptr
, /* Pointer into output buffer */
1632 *dstend
; /* End of output buffer */
1633 int saw_8859
= 0; /* Saw an extended character that was not UTF-8? */
1636 for (srcptr
= (unsigned char *)src
, dstptr
= (unsigned char *)dst
, dstend
= dstptr
+ dstsize
- 1; *srcptr
;)
1639 *dstptr
++ = *srcptr
++; /* ASCII */
1643 * Map ISO-8859-1 (most likely character set for legacy LPD clients) to
1647 if (dstptr
> (dstend
- 2))
1650 *dstptr
++ = 0xc0 | (*srcptr
>> 6);
1651 *dstptr
++ = 0x80 | (*srcptr
++ & 0x3f);
1653 else if ((*srcptr
& 0xe0) == 0xc0 && (srcptr
[1] & 0xc0) == 0x80)
1656 * 2-byte UTF-8 sequence...
1659 if (dstptr
> (dstend
- 2))
1662 *dstptr
++ = *srcptr
++;
1663 *dstptr
++ = *srcptr
++;
1665 else if ((*srcptr
& 0xf0) == 0xe0 && (srcptr
[1] & 0xc0) == 0x80 && (srcptr
[2] & 0xc0) == 0x80)
1668 * 3-byte UTF-8 sequence...
1671 if (dstptr
> (dstend
- 3))
1674 *dstptr
++ = *srcptr
++;
1675 *dstptr
++ = *srcptr
++;
1676 *dstptr
++ = *srcptr
++;
1678 else if ((*srcptr
& 0xf8) == 0xf0 && (srcptr
[1] & 0xc0) == 0x80 && (srcptr
[2] & 0xc0) == 0x80 && (srcptr
[3] & 0xc0) == 0x80)
1681 * 4-byte UTF-8 sequence...
1684 if (dstptr
> (dstend
- 4))
1687 *dstptr
++ = *srcptr
++;
1688 *dstptr
++ = *srcptr
++;
1689 *dstptr
++ = *srcptr
++;
1690 *dstptr
++ = *srcptr
++;
1695 * Bad UTF-8 sequence, this must be an ISO-8859-1 string...
1700 if (dstptr
> (dstend
- 2))
1703 *dstptr
++ = 0xc0 | (*srcptr
>> 6);
1704 *dstptr
++ = 0x80 | (*srcptr
++ & 0x3f);