2 * "$Id: cups-lpd.c 6022 2006-10-10 19:47:03Z mike $"
4 * Line Printer Daemon interface for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE.txt" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
26 * main() - Process an incoming LPD request...
27 * create_job() - Create a new print job.
28 * get_printer() - Get the named printer and its options.
29 * print_file() - Add a file to the current job.
30 * recv_print_job() - Receive a print job from the client.
31 * remove_jobs() - Cancel one or more jobs.
32 * send_state() - Send the queue state.
33 * smart_gets() - Get a line of text, removing the trailing CR and/or LF.
37 * Include necessary headers...
40 #include <cups/http-private.h>
41 #include <cups/cups.h>
42 #include <cups/string.h>
43 #include <cups/language.h>
51 #include <sys/types.h>
52 #include <sys/socket.h>
53 #include <netinet/in.h>
56 #ifdef HAVE_INTTYPES_H
57 # include <inttypes.h>
58 #endif /* HAVE_INTTYPES_H */
60 #ifdef HAVE_COREFOUNDATION_H
61 # include <CoreFoundation/CoreFoundation.h>
62 #endif /* HAVE_COREFOUNDATION_H */
64 # include <CoreFoundation/CFPriv.h>
65 #endif /* HAVE_CFPRIV_H */
69 * LPD "mini-daemon" for CUPS. This program must be used in conjunction
70 * with inetd or another similar program that monitors ports and starts
71 * daemons for each client connection. A typical configuration is:
73 * printer stream tcp nowait lp /usr/lib/cups/daemon/cups-lpd cups-lpd
75 * This daemon implements most of RFC 1179 (the unofficial LPD specification)
78 * - This daemon does not check to make sure that the source port is
79 * between 721 and 731, since it isn't necessary for proper
80 * functioning and port-based security is no security at all!
82 * - The "Print any waiting jobs" command is a no-op.
84 * The LPD-to-IPP mapping is as defined in RFC 2569. The report formats
85 * currently match the Solaris LPD mini-daemon.
92 static int create_job(http_t
*http
, const char *dest
, const char *title
,
93 const char *user
, int num_options
,
94 cups_option_t
*options
);
95 static int get_printer(http_t
*http
, const char *name
, char *dest
,
96 int destsize
, cups_option_t
**options
,
97 int *accepting
, int *shared
, ipp_pstate_t
*state
);
98 static int print_file(http_t
*http
, int id
, const char *filename
,
99 const char *docname
, const char *user
, int last
);
100 static int recv_print_job(const char *name
, int num_defaults
,
101 cups_option_t
*defaults
);
102 static int remove_jobs(const char *name
, const char *agent
,
104 static int send_state(const char *name
, const char *list
,
106 static char *smart_gets(char *s
, int len
, FILE *fp
);
110 * 'main()' - Process an incoming LPD request...
113 int /* O - Exit status */
114 main(int argc
, /* I - Number of command-line arguments */
115 char *argv
[]) /* I - Command-line arguments */
117 int i
; /* Looping var */
118 int num_defaults
; /* Number of default options */
119 cups_option_t
*defaults
; /* Default options */
120 char line
[256], /* Command string */
121 command
, /* Command code */
122 *dest
, /* Pointer to destination */
123 *list
, /* Pointer to list */
124 *agent
, /* Pointer to user */
125 status
; /* Status for client */
126 socklen_t hostlen
; /* Size of client address */
127 http_addr_t hostaddr
; /* Address of client */
128 char hostname
[256], /* Name of client */
129 hostip
[256], /* IP address */
130 *hostfamily
; /* Address family */
131 int hostlookups
; /* Do hostname lookups? */
135 * Don't buffer the output...
138 setbuf(stdout
, NULL
);
141 * Log things using the "cups-lpd" name...
144 openlog("cups-lpd", LOG_PID
, LOG_LPR
);
147 * Scan the command-line for options...
154 for (i
= 1; i
< argc
; i
++)
155 if (argv
[i
][0] == '-')
159 case 'o' : /* Option */
161 num_defaults
= cupsParseOptions(argv
[i
] + 2, num_defaults
,
167 num_defaults
= cupsParseOptions(argv
[i
], num_defaults
,
170 syslog(LOG_WARNING
, "Expected option string after -o option!");
174 case 'n' : /* Don't do hostname lookups */
179 syslog(LOG_WARNING
, "Unknown option \"%c\" ignored!", argv
[i
][1]);
184 syslog(LOG_WARNING
, "Unknown command-line option \"%s\" ignored!",
188 * Get the address of the client...
191 hostlen
= sizeof(hostaddr
);
193 if (getpeername(0, (struct sockaddr
*)&hostaddr
, &hostlen
))
195 syslog(LOG_WARNING
, "Unable to get client address - %s", strerror(errno
));
196 strcpy(hostname
, "unknown");
200 httpAddrString(&hostaddr
, hostip
, sizeof(hostip
));
203 httpAddrLookup(&hostaddr
, hostname
, sizeof(hostname
));
205 strlcpy(hostname
, hostip
, sizeof(hostname
));
208 if (hostaddr
.addr
.sa_family
== AF_INET6
)
211 #endif /* AF_INET6 */
214 syslog(LOG_INFO
, "Connection from %s (%s %s)", hostname
, hostfamily
,
218 num_defaults
= cupsAddOption("job-originating-host-name", hostname
,
219 num_defaults
, &defaults
);
222 * RFC1179 specifies that only 1 daemon command can be received for
226 if (smart_gets(line
, sizeof(line
), stdin
) == NULL
)
229 * Unable to get command from client! Send an error status and return.
232 syslog(LOG_ERR
, "Unable to get command line from client!");
238 * The first byte is the command byte. After that will be the queue name,
239 * resource list, and/or user name.
249 for (list
= dest
+ 1; *list
&& !isspace(*list
& 255); list
++);
251 while (isspace(*list
& 255))
261 default : /* Unknown command */
262 syslog(LOG_ERR
, "Unknown LPD command 0x%02X!", command
);
263 syslog(LOG_ERR
, "Command line = %s", line
+ 1);
269 case 0x01 : /* Print any waiting jobs */
270 syslog(LOG_INFO
, "Print waiting jobs (no-op)");
276 case 0x02 : /* Receive a printer job */
277 syslog(LOG_INFO
, "Receive print job for %s", dest
);
278 /* recv_print_job() sends initial status byte */
280 status
= recv_print_job(dest
, num_defaults
, defaults
);
283 case 0x03 : /* Send queue state (short) */
284 syslog(LOG_INFO
, "Send queue state (short) for %s %s", dest
, list
);
285 /* no status byte for this command */
287 status
= send_state(dest
, list
, 0);
290 case 0x04 : /* Send queue state (long) */
291 syslog(LOG_INFO
, "Send queue state (long) for %s %s", dest
, list
);
292 /* no status byte for this command */
294 status
= send_state(dest
, list
, 1);
297 case 0x05 : /* Remove jobs */
299 * Grab the agent and skip to the list of users and/or jobs.
304 for (; *list
&& !isspace(*list
& 255); list
++);
305 while (isspace(*list
& 255))
308 syslog(LOG_INFO
, "Remove jobs %s on %s by %s", list
, dest
, agent
);
310 status
= remove_jobs(dest
, agent
, list
);
316 syslog(LOG_INFO
, "Closing connection");
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_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_OK_CONFLICT
)
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 int 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_PRINTER_STOPPED
;
452 * If the queue name contains a space, lookup the printer-name using
453 * the printer-info value...
456 if (strchr(name
, ' '))
459 * Lookup the printer-info...
462 ipp_attribute_t
*accepting_attr
,/* printer-is-accepting-jobs */
463 *info_attr
, /* printer-info */
464 *name_attr
, /* printer-name */
465 *shared_attr
, /* printer-is-shared */
466 *state_attr
; /* printer-state */
470 * Setup the CUPS-Get-Printers request...
473 request
= ippNewRequest(CUPS_GET_PRINTERS
);
475 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
476 "requested-attributes",
477 (int)(sizeof(requested
) / sizeof(requested
[0])),
484 response
= cupsDoRequest(http
, request
, "/");
486 if (!response
|| cupsLastError() > IPP_OK_CONFLICT
)
488 syslog(LOG_ERR
, "Unable to get list of printers - %s",
489 cupsLastErrorString());
497 * Scan the response for printers...
501 attr
= response
->attrs
;
506 * Skip to the next printer...
509 while (attr
&& attr
->group_tag
!= IPP_TAG_PRINTER
)
516 * Get all of the attributes for the current printer...
519 accepting_attr
= NULL
;
525 while (attr
&& attr
->group_tag
== IPP_TAG_PRINTER
)
527 if (!strcmp(attr
->name
, "printer-is-accepting-jobs") &&
528 attr
->value_tag
== IPP_TAG_BOOLEAN
)
529 accepting_attr
= attr
;
530 else if (!strcmp(attr
->name
, "printer-info") &&
531 attr
->value_tag
== IPP_TAG_TEXT
)
533 else if (!strcmp(attr
->name
, "printer-name") &&
534 attr
->value_tag
== IPP_TAG_NAME
)
536 else if (!strcmp(attr
->name
, "printer-is-shared") &&
537 attr
->value_tag
== IPP_TAG_BOOLEAN
)
539 else if (!strcmp(attr
->name
, "printer-state") &&
540 attr
->value_tag
== IPP_TAG_ENUM
)
546 if (info_attr
&& name_attr
&&
547 !strcasecmp(name
, info_attr
->values
[0].string
.text
))
550 * Found a match, use this one!
553 strlcpy(dest
, name_attr
->values
[0].string
.text
, destsize
);
555 if (accepting
&& accepting_attr
)
556 *accepting
= accepting_attr
->values
[0].boolean
;
558 if (shared
&& shared_attr
)
559 *shared
= shared_attr
->values
[0].boolean
;
561 if (state
&& state_attr
)
562 *state
= (ipp_pstate_t
)state_attr
->values
[0].integer
;
572 syslog(LOG_ERR
, "Unable to find \"%s\" in list of printers!", name
);
582 * Otherwise treat it as a queue name optionally with an instance name.
585 strlcpy(dest
, name
, destsize
);
586 if ((value
= strchr(dest
, '/')) != NULL
)
590 * Setup the Get-Printer-Attributes request...
593 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
595 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
596 "localhost", 0, "/printers/%s", dest
);
598 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
601 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
602 "requested-attributes",
603 (int)(sizeof(requested
) / sizeof(requested
[0])),
610 response
= cupsDoRequest(http
, request
, "/");
612 if (!response
|| cupsLastError() > IPP_OK_CONFLICT
)
614 syslog(LOG_ERR
, "Unable to check printer status - %s",
615 cupsLastErrorString());
623 * Get values from the response...
628 if ((attr
= ippFindAttribute(response
, "printer-is-accepting-jobs",
629 IPP_TAG_BOOLEAN
)) == NULL
)
630 syslog(LOG_ERR
, "No printer-is-accepting-jobs attribute found in "
631 "response from server!");
633 *accepting
= attr
->values
[0].boolean
;
638 if ((attr
= ippFindAttribute(response
, "printer-is-shared",
639 IPP_TAG_BOOLEAN
)) == NULL
)
641 syslog(LOG_ERR
, "No printer-is-shared attribute found in "
642 "response from server!");
646 *shared
= attr
->values
[0].boolean
;
651 if ((attr
= ippFindAttribute(response
, "printer-state",
652 IPP_TAG_ENUM
)) == NULL
)
653 syslog(LOG_ERR
, "No printer-state attribute found in "
654 "response from server!");
656 *state
= (ipp_pstate_t
)attr
->values
[0].integer
;
663 * Override shared value for LPD using system-specific APIs...
666 #ifdef HAVE_CFPRIV_H /* MacOS X */
667 if (shared
&& *shared
)
669 CFURLRef prefsurl
; /* */
670 CFDataRef xmldata
; /* */
671 CFPropertyListRef plist
; /* */
672 CFStringRef queueid
; /* */
673 CFArrayRef lprqarray
; /* */
674 CFBooleanRef serverflag
; /* */
675 Boolean prefsok
; /* */
676 static const char printerprefsfile
[] =
677 "/Library/Preferences/com.apple.printservice.plist";
678 /* Preferences file */
682 * See if we are running on MacOS X Server...
685 CFDictionaryRef versdict
= _CFCopyServerVersionDictionary();
690 * Yes, use the LPR sharing preference...
697 prefsurl
= CFURLCreateFromFileSystemRepresentation(
699 (const UInt8
*)printerprefsfile
,
700 (CFIndex
)strlen(printerprefsfile
),
704 prefsok
= CFURLCreateDataAndPropertiesFromResource(
705 kCFAllocatorDefault
, prefsurl
, &xmldata
,
709 plist
= CFPropertyListCreateFromXMLData(kCFAllocatorDefault
, xmldata
,
710 kCFPropertyListImmutable
, NULL
);
713 serverflag
= (CFBooleanRef
)CFDictionaryGetValue(
714 (CFDictionaryRef
)plist
, CFSTR("serviceState"));
716 if (serverflag
&& CFBooleanGetValue(serverflag
))
718 lprqarray
= (CFArrayRef
)CFDictionaryGetValue(
719 (CFDictionaryRef
)plist
, CFSTR("lprSharedQueues"));
723 queueid
= CFStringCreateWithCString(CFAllocatorGetDefault(),
725 kCFStringEncodingUTF8
);
729 *shared
= CFArrayContainsValue(
731 CFRangeMake(0, CFArrayGetCount(lprqarray
)),
737 CFRelease(lprqarray
);
742 CFRelease(serverflag
);
752 syslog(LOG_ERR
, "Warning - Print Service sharing disabled for LPD "
753 "on queue: %s", name
);
756 #endif /* HAVE_CFPRIV_H */
759 * Next look for the printer in the lpoptions file...
764 if (options
&& shared
&& accepting
)
766 if ((cups_serverroot
= getenv("CUPS_SERVERROOT")) == NULL
)
767 cups_serverroot
= CUPS_SERVERROOT
;
769 snprintf(line
, sizeof(line
), "%s/lpoptions", cups_serverroot
);
770 if ((fp
= cupsFileOpen(line
, "r")) != NULL
)
773 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
776 * Make sure we have "Dest name options" or "Default name options"...
779 if ((strcasecmp(line
, "Dest") && strcasecmp(line
, "Default")) || !value
)
783 * Separate destination name from options...
786 for (optptr
= value
; *optptr
&& !isspace(*optptr
& 255); optptr
++);
788 while (*optptr
== ' ')
792 * If this is our destination, parse the options and break out of
793 * the loop - we're done!
796 if (!strcasecmp(value
, name
))
798 num_options
= cupsParseOptions(optptr
, num_options
, options
);
810 * Return the number of options for this destination...
813 return (num_options
);
818 * 'print_file()' - Add a file to the current job.
821 static int /* O - 0 on success, -1 on failure */
822 print_file(http_t
*http
, /* I - HTTP connection */
823 int id
, /* I - Job ID */
824 const char *filename
, /* I - File to print */
825 const char *docname
, /* I - document-name */
826 const char *user
, /* I - requesting-user-name */
827 int last
) /* I - 1 = last file in job */
829 ipp_t
*request
; /* IPP request */
830 char uri
[HTTP_MAX_URI
]; /* Printer URI */
834 * Setup the Send-Document request...
837 request
= ippNewRequest(IPP_SEND_DOCUMENT
);
839 snprintf(uri
, sizeof(uri
), "ipp://localhost/jobs/%d", id
);
840 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
842 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
843 "requesting-user-name", NULL
, user
);
846 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
847 "document-name", NULL
, docname
);
850 ippAddBoolean(request
, IPP_TAG_OPERATION
, "last-document", 1);
856 snprintf(uri
, sizeof(uri
), "/jobs/%d", id
);
858 ippDelete(cupsDoFileRequest(http
, request
, uri
, filename
));
860 if (cupsLastError() > IPP_OK_CONFLICT
)
862 syslog(LOG_ERR
, "Unable to send document - %s", cupsLastErrorString());
872 * 'recv_print_job()' - Receive a print job from the client.
875 static int /* O - Command status */
877 const char *queue
, /* I - Printer name */
878 int num_defaults
, /* I - Number of default options */
879 cups_option_t
*defaults
) /* I - Default options */
881 http_t
*http
; /* HTTP connection */
882 int i
; /* Looping var */
883 int status
; /* Command status */
884 int fd
; /* Temporary file */
885 FILE *fp
; /* File pointer */
886 char filename
[1024]; /* Temporary filename */
887 int bytes
; /* Bytes received */
888 char line
[256], /* Line from file/stdin */
889 command
, /* Command from line */
890 *count
, /* Number of bytes */
891 *name
; /* Name of file */
892 const char *job_sheets
; /* Job sheets */
893 int num_data
; /* Number of data files */
894 char control
[1024], /* Control filename */
895 data
[100][256], /* Data files */
896 temp
[100][1024]; /* Temporary files */
897 char user
[1024], /* User name */
898 title
[1024], /* Job title */
899 docname
[1024], /* Document name */
900 dest
[256]; /* Printer/class queue */
901 int accepting
, /* printer-is-accepting */
902 shared
, /* printer-is-shared */
903 num_options
; /* Number of options */
904 cups_option_t
*options
; /* Options */
906 int docnumber
, /* Current document number */
907 doccount
; /* Count of documents */
911 * Connect to the server...
914 http
= httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
917 syslog(LOG_ERR
, "Unable to connect to server: %s", strerror(errno
));
925 * See if the printer is available...
928 num_options
= get_printer(http
, queue
, dest
, sizeof(dest
), &options
,
929 &accepting
, &shared
, NULL
);
931 if (num_options
< 0 || !accepting
|| !shared
)
934 syslog(LOG_INFO
, "Rejecting job because \"%s\" is not %s", dest
,
935 !accepting
? "accepting jobs" : "shared");
937 syslog(LOG_ERR
, "Unable to get printer information for \"%s\"", queue
);
946 putchar(0); /* OK so far... */
949 * Read the request...
958 while (smart_gets(line
, sizeof(line
), stdin
) != NULL
)
960 if (strlen(line
) < 2)
969 for (name
= count
+ 1; *name
&& !isspace(*name
& 255); name
++);
970 while (isspace(*name
& 255))
976 case 0x01 : /* Abort */
980 case 0x02 : /* Receive control file */
981 if (strlen(name
) < 2)
983 syslog(LOG_ERR
, "Bad control file name \"%s\"", name
);
992 * Append to the existing control file - the LPD spec is
993 * not entirely clear, but at least the OS/2 LPD code sends
994 * multiple control files per connection...
997 if ((fd
= open(control
, O_WRONLY
)) < 0)
1000 "Unable to append to temporary control file \"%s\" - %s",
1001 control
, strerror(errno
));
1007 lseek(fd
, 0, SEEK_END
);
1011 if ((fd
= cupsTempFd(control
, sizeof(control
))) < 0)
1013 syslog(LOG_ERR
, "Unable to open temporary control file \"%s\" - %s",
1014 control
, strerror(errno
));
1020 strcpy(filename
, control
);
1024 case 0x03 : /* Receive data file */
1025 if (strlen(name
) < 2)
1027 syslog(LOG_ERR
, "Bad data file name \"%s\"", name
);
1033 if (num_data
>= (int)(sizeof(data
) / sizeof(data
[0])))
1036 * Too many data files...
1039 syslog(LOG_ERR
, "Too many data files (%d)", num_data
);
1045 strlcpy(data
[num_data
], name
, sizeof(data
[0]));
1047 if ((fd
= cupsTempFd(temp
[num_data
], sizeof(temp
[0]))) < 0)
1049 syslog(LOG_ERR
, "Unable to open temporary data file \"%s\" - %s",
1050 temp
[num_data
], strerror(errno
));
1056 strcpy(filename
, temp
[num_data
]);
1068 * Copy the data or control file from the client...
1071 for (i
= atoi(count
); i
> 0; i
-= bytes
)
1073 if (i
> sizeof(line
))
1074 bytes
= sizeof(line
);
1078 if ((bytes
= fread(line
, 1, bytes
, stdin
)) > 0)
1079 bytes
= write(fd
, line
, bytes
);
1083 syslog(LOG_ERR
, "Error while reading file - %s",
1091 * Read trailing nul...
1096 if (fread(line
, 1, 1, stdin
) < 1)
1099 syslog(LOG_ERR
, "Error while reading trailing nul - %s",
1105 syslog(LOG_ERR
, "Trailing character after file is not nul (%02X)!",
1111 * Close the file and send an acknowledgement...
1125 * Process the control file and print stuff...
1128 if ((fp
= fopen(control
, "rb")) == NULL
)
1133 * Copy the default options...
1136 for (i
= 0; i
< num_defaults
; i
++)
1137 num_options
= cupsAddOption(defaults
[i
].name
,
1139 num_options
, &options
);
1142 * Grab the job information...
1149 while (smart_gets(line
, sizeof(line
), fp
) != NULL
)
1152 * Process control lines...
1157 case 'J' : /* Job name */
1158 strlcpy(title
, line
+ 1, sizeof(title
));
1161 case 'P' : /* User identification */
1162 strlcpy(user
, line
+ 1, sizeof(user
));
1165 case 'L' : /* Print banner page */
1167 * If a banner was requested and it's not overridden by a
1168 * command line option and the destination's default is none
1169 * then add the standard banner...
1172 if (cupsGetOption("job-sheets", num_defaults
, defaults
) == NULL
&&
1173 ((job_sheets
= cupsGetOption("job-sheets", num_options
,
1174 options
)) == NULL
||
1175 !strcmp(job_sheets
, "none,none")))
1177 num_options
= cupsAddOption("job-sheets", "standard",
1178 num_options
, &options
);
1182 case 'c' : /* Plot CIF file */
1183 case 'd' : /* Print DVI file */
1184 case 'f' : /* Print formatted file */
1185 case 'g' : /* Plot file */
1186 case 'l' : /* Print file leaving control characters (raw) */
1187 case 'n' : /* Print ditroff output file */
1188 case 'o' : /* Print PostScript output file */
1189 case 'p' : /* Print file with 'pr' format (prettyprint) */
1190 case 'r' : /* File to print with FORTRAN carriage control */
1191 case 't' : /* Print troff output file */
1192 case 'v' : /* Print raster file */
1195 if (line
[0] == 'l' &&
1196 !cupsGetOption("document-format", num_options
, options
))
1197 num_options
= cupsAddOption("raw", "", num_options
, &options
);
1200 num_options
= cupsAddOption("prettyprint", "", num_options
,
1210 * Check that we have a username...
1215 syslog(LOG_WARNING
, "No username specified by client! "
1216 "Using \"anonymous\"...");
1217 strcpy(user
, "anonymous");
1224 if ((id
= create_job(http
, dest
, title
, user
, num_options
, options
)) < 0)
1229 * Then print the job files...
1237 while (smart_gets(line
, sizeof(line
), fp
) != NULL
)
1240 * Process control lines...
1245 case 'N' : /* Document name */
1246 strlcpy(docname
, line
+ 1, sizeof(docname
));
1249 case 'c' : /* Plot CIF file */
1250 case 'd' : /* Print DVI file */
1251 case 'f' : /* Print formatted file */
1252 case 'g' : /* Plot file */
1253 case 'l' : /* Print file leaving control characters (raw) */
1254 case 'n' : /* Print ditroff output file */
1255 case 'o' : /* Print PostScript output file */
1256 case 'p' : /* Print file with 'pr' format (prettyprint) */
1257 case 'r' : /* File to print with FORTRAN carriage control */
1258 case 't' : /* Print troff output file */
1259 case 'v' : /* Print raster file */
1261 * Figure out which file we are printing...
1264 for (i
= 0; i
< num_data
; i
++)
1265 if (!strcmp(data
[i
], line
+ 1))
1275 * Send the print file...
1280 if (print_file(http
, id
, temp
[i
], docname
, user
,
1281 docnumber
== doccount
))
1298 cupsFreeOptions(num_options
, options
);
1303 * Clean up all temporary files and return...
1308 for (i
= 0; i
< num_data
; i
++)
1316 * 'remove_jobs()' - Cancel one or more jobs.
1319 static int /* O - Command status */
1320 remove_jobs(const char *dest
, /* I - Destination */
1321 const char *agent
, /* I - User agent */
1322 const char *list
) /* I - List of jobs or users */
1324 int id
; /* Job ID */
1325 http_t
*http
; /* HTTP server connection */
1326 ipp_t
*request
; /* IPP Request */
1327 char uri
[HTTP_MAX_URI
]; /* Job URI */
1330 (void)dest
; /* Suppress compiler warnings... */
1333 * Try connecting to the local server...
1336 if ((http
= httpConnectEncrypt(cupsServer(), ippPort(),
1337 cupsEncryption())) == NULL
)
1339 syslog(LOG_ERR
, "Unable to connect to server %s: %s", cupsServer(),
1345 * Loop for each job...
1348 while ((id
= atoi(list
)) > 0)
1351 * Skip job ID in list...
1354 while (isdigit(*list
& 255))
1356 while (isspace(*list
& 255))
1360 * Build an IPP_CANCEL_JOB request, which requires the following
1363 * attributes-charset
1364 * attributes-natural-language
1366 * requesting-user-name
1369 request
= ippNewRequest(IPP_CANCEL_JOB
);
1371 sprintf(uri
, "ipp://localhost/jobs/%d", id
);
1372 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1374 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1375 "requesting-user-name", NULL
, agent
);
1378 * Do the request and get back a response...
1381 ippDelete(cupsDoRequest(http
, request
, "/jobs"));
1383 if (cupsLastError() > IPP_OK_CONFLICT
)
1385 syslog(LOG_WARNING
, "Cancel of job ID %d failed: %s\n", id
,
1386 cupsLastErrorString());
1391 syslog(LOG_INFO
, "Job ID %d canceled", id
);
1401 * 'send_state()' - Send the queue state.
1404 static int /* O - Command status */
1405 send_state(const char *queue
, /* I - Destination */
1406 const char *list
, /* I - Job or user */
1407 int longstatus
) /* I - List of jobs or users */
1409 int id
; /* Job ID from list */
1410 http_t
*http
; /* HTTP server connection */
1411 ipp_t
*request
, /* IPP Request */
1412 *response
; /* IPP Response */
1413 ipp_attribute_t
*attr
; /* Current attribute */
1414 ipp_pstate_t state
; /* Printer state */
1415 const char *jobdest
, /* Pointer into job-printer-uri */
1416 *jobuser
, /* Pointer to job-originating-user-name */
1417 *jobname
; /* Pointer to job-name */
1418 ipp_jstate_t jobstate
; /* job-state */
1419 int jobid
, /* job-id */
1420 jobsize
, /* job-k-octets */
1421 jobcount
, /* Number of jobs */
1422 jobcopies
, /* Number of copies */
1423 rank
; /* Rank of job */
1424 char rankstr
[255]; /* Rank string */
1425 char namestr
[1024]; /* Job name string */
1426 char uri
[HTTP_MAX_URI
]; /* Printer URI */
1427 char dest
[256]; /* Printer/class queue */
1428 static const char * const ranks
[10] = /* Ranking strings */
1441 static const char * const requested
[] =
1442 { /* Requested attributes */
1447 "job-originating-user-name",
1454 * Try connecting to the local server...
1457 if ((http
= httpConnectEncrypt(cupsServer(), ippPort(),
1458 cupsEncryption())) == NULL
)
1460 syslog(LOG_ERR
, "Unable to connect to server %s: %s", cupsServer(),
1462 printf("Unable to connect to server %s: %s", cupsServer(), strerror(errno
));
1467 * Get the actual destination name and printer state...
1470 if (get_printer(http
, queue
, dest
, sizeof(dest
), NULL
, NULL
, NULL
, &state
))
1472 syslog(LOG_ERR
, "Unable to get printer %s: %s", queue
,
1473 cupsLastErrorString());
1474 printf("Unable to get printer %s: %s", queue
, cupsLastErrorString());
1479 * Show the queue state...
1484 case IPP_PRINTER_IDLE
:
1485 printf("%s is ready\n", dest
);
1487 case IPP_PRINTER_PROCESSING
:
1488 printf("%s is ready and printing\n", dest
);
1490 case IPP_PRINTER_STOPPED
:
1491 printf("%s is not ready\n", dest
);
1496 * Build an IPP_GET_JOBS or IPP_GET_JOB_ATTRIBUTES request, which requires
1497 * the following attributes:
1499 * attributes-charset
1500 * attributes-natural-language
1501 * job-uri or printer-uri
1506 request
= ippNewRequest(id
? IPP_GET_JOB_ATTRIBUTES
: IPP_GET_JOBS
);
1508 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1509 "localhost", 0, "/printers/%s", dest
);
1511 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1515 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", id
);
1518 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1519 "requesting-user-name", NULL
, list
);
1520 ippAddBoolean(request
, IPP_TAG_OPERATION
, "my-jobs", 1);
1523 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1524 "requested-attributes",
1525 sizeof(requested
) / sizeof(requested
[0]),
1529 * Do the request and get back a response...
1533 response
= cupsDoRequest(http
, request
, "/");
1535 if (cupsLastError() > IPP_OK_CONFLICT
)
1537 printf("get-jobs failed: %s\n", cupsLastErrorString());
1538 ippDelete(response
);
1543 * Loop through the job list and display them...
1546 for (attr
= response
->attrs
, rank
= 1; attr
; attr
= attr
->next
)
1549 * Skip leading attributes until we hit a job...
1552 while (attr
&& (attr
->group_tag
!= IPP_TAG_JOB
|| !attr
->name
))
1559 * Pull the needed attributes from this job...
1564 jobstate
= IPP_JOB_PENDING
;
1565 jobname
= "untitled";
1570 while (attr
&& attr
->group_tag
== IPP_TAG_JOB
)
1572 if (!strcmp(attr
->name
, "job-id") &&
1573 attr
->value_tag
== IPP_TAG_INTEGER
)
1574 jobid
= attr
->values
[0].integer
;
1576 if (!strcmp(attr
->name
, "job-k-octets") &&
1577 attr
->value_tag
== IPP_TAG_INTEGER
)
1578 jobsize
= attr
->values
[0].integer
;
1580 if (!strcmp(attr
->name
, "job-state") &&
1581 attr
->value_tag
== IPP_TAG_ENUM
)
1582 jobstate
= (ipp_jstate_t
)attr
->values
[0].integer
;
1584 if (!strcmp(attr
->name
, "job-printer-uri") &&
1585 attr
->value_tag
== IPP_TAG_URI
)
1586 if ((jobdest
= strrchr(attr
->values
[0].string
.text
, '/')) != NULL
)
1589 if (!strcmp(attr
->name
, "job-originating-user-name") &&
1590 attr
->value_tag
== IPP_TAG_NAME
)
1591 jobuser
= attr
->values
[0].string
.text
;
1593 if (!strcmp(attr
->name
, "job-name") &&
1594 attr
->value_tag
== IPP_TAG_NAME
)
1595 jobname
= attr
->values
[0].string
.text
;
1597 if (!strcmp(attr
->name
, "copies") &&
1598 attr
->value_tag
== IPP_TAG_INTEGER
)
1599 jobcopies
= attr
->values
[0].integer
;
1605 * See if we have everything needed...
1608 if (!jobdest
|| !jobid
)
1616 if (!longstatus
&& jobcount
== 0)
1617 puts("Rank Owner Job File(s) Total Size");
1622 * Display the job...
1625 if (jobstate
== IPP_JOB_PROCESSING
)
1626 strcpy(rankstr
, "active");
1629 snprintf(rankstr
, sizeof(rankstr
), "%d%s", rank
, ranks
[rank
% 10]);
1638 snprintf(namestr
, sizeof(namestr
), "%d copies of %s", jobcopies
,
1641 strlcpy(namestr
, jobname
, sizeof(namestr
));
1643 printf("%s: %-33.33s [job %d localhost]\n", jobuser
, rankstr
, jobid
);
1644 printf(" %-39.39s %.0f bytes\n", namestr
, 1024.0 * jobsize
);
1647 printf("%-7s %-7.7s %-7d %-31.31s %.0f bytes\n", rankstr
, jobuser
,
1648 jobid
, jobname
, 1024.0 * jobsize
);
1654 ippDelete(response
);
1666 * 'smart_gets()' - Get a line of text, removing the trailing CR and/or LF.
1669 static char * /* O - Line read or NULL */
1670 smart_gets(char *s
, /* I - Pointer to line buffer */
1671 int len
, /* I - Size of line buffer */
1672 FILE *fp
) /* I - File to read from */
1674 char *ptr
, /* Pointer into line */
1675 *end
; /* End of line */
1676 int ch
; /* Character from file */
1680 * Read the line; unlike fgets(), we read the entire line but dump
1681 * characters that go past the end of the buffer. Also, we accept
1682 * CR, LF, or CR LF for the line endings to be "safe", although
1683 * RFC 1179 specifically says "just use LF".
1689 while ((ch
= getc(fp
)) != EOF
)
1693 else if (ch
== '\r')
1696 * See if a LF follows...
1712 if (ch
== EOF
&& ptr
== s
)
1720 * End of "$Id: cups-lpd.c 6022 2006-10-10 19:47:03Z mike $".