2 * "$Id: cups-lpd.c 5049 2006-02-02 14:50:57Z mike $"
4 * Line Printer Daemon interface for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2005 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 * print_file() - Print a file to a printer or class.
28 * recv_print_job() - Receive a print job from the client.
29 * remove_jobs() - Cancel one or more jobs.
30 * send_state() - Send the queue state.
31 * smart_gets() - Get a line of text, removing the trailing CR
36 * Include necessary headers...
39 #include <cups/http-private.h>
40 #include <cups/cups.h>
41 #include <cups/string.h>
42 #include <cups/language.h>
50 #include <sys/types.h>
51 #include <sys/socket.h>
52 #include <netinet/in.h>
57 * LPD "mini-daemon" for CUPS. This program must be used in conjunction
58 * with inetd or another similar program that monitors ports and starts
59 * daemons for each client connection. A typical configuration is:
61 * printer stream tcp nowait lp /usr/lib/cups/daemon/cups-lpd cups-lpd
63 * This daemon implements most of RFC 1179 (the unofficial LPD specification)
66 * - This daemon does not check to make sure that the source port is
67 * between 721 and 731, since it isn't necessary for proper
68 * functioning and port-based security is no security at all!
70 * - The "Print any waiting jobs" command is a no-op.
72 * The LPD-to-IPP mapping is as defined in RFC 2569. The report formats
73 * currently match the Solaris LPD mini-daemon.
80 int print_file(const char *name
, const char *file
,
81 const char *title
, const char *docname
,
82 const char *user
, int num_options
,
83 cups_option_t
*options
);
84 int recv_print_job(const char *dest
, int num_defaults
, cups_option_t
*defaults
);
85 int remove_jobs(const char *dest
, const char *agent
, const char *list
);
86 int send_state(const char *dest
, const char *list
, int longstatus
);
87 char *smart_gets(char *s
, int len
, FILE *fp
);
91 * 'main()' - Process an incoming LPD request...
94 int /* O - Exit status */
95 main(int argc
, /* I - Number of command-line arguments */
96 char *argv
[]) /* I - Command-line arguments */
98 int i
; /* Looping var */
99 int num_defaults
; /* Number of default options */
100 cups_option_t
*defaults
; /* Default options */
101 char line
[256], /* Command string */
102 command
, /* Command code */
103 *dest
, /* Pointer to destination */
104 *list
, /* Pointer to list */
105 *agent
, /* Pointer to user */
106 status
; /* Status for client */
107 socklen_t hostlen
; /* Size of client address */
108 http_addr_t hostaddr
; /* Address of client */
109 char hostname
[256], /* Name of client */
110 hostip
[256], /* IP address */
111 *hostfamily
; /* Address family */
115 * Don't buffer the output...
118 setbuf(stdout
, NULL
);
121 * Log things using the "cups-lpd" name...
124 openlog("cups-lpd", LOG_PID
, LOG_LPR
);
127 * Get the address of the client...
130 hostlen
= sizeof(hostaddr
);
132 if (getpeername(0, (struct sockaddr
*)&hostaddr
, &hostlen
))
134 syslog(LOG_WARNING
, "Unable to get client address - %s", strerror(errno
));
135 strcpy(hostname
, "unknown");
139 httpAddrLookup(&hostaddr
, hostname
, sizeof(hostname
));
140 httpAddrString(&hostaddr
, hostip
, sizeof(hostip
));
143 if (hostaddr
.addr
.sa_family
== AF_INET6
)
146 #endif /* AF_INET6 */
149 syslog(LOG_INFO
, "Connection from %s (%s %s)", hostname
, hostfamily
, hostip
);
153 * Scan the command-line for options...
159 num_defaults
= cupsAddOption("job-originating-host-name", hostname
,
160 num_defaults
, &defaults
);
162 for (i
= 1; i
< argc
; i
++)
163 if (argv
[i
][0] == '-')
167 case 'o' : /* Option */
169 num_defaults
= cupsParseOptions(argv
[i
] + 2, num_defaults
,
175 num_defaults
= cupsParseOptions(argv
[i
], num_defaults
, &defaults
);
177 syslog(LOG_WARNING
, "Expected option string after -o option!");
181 syslog(LOG_WARNING
, "Unknown option \"%c\" ignored!", argv
[i
][1]);
186 syslog(LOG_WARNING
, "Unknown command-line option \"%s\" ignored!", argv
[i
]);
189 * RFC1179 specifies that only 1 daemon command can be received for
193 if (smart_gets(line
, sizeof(line
), stdin
) == NULL
)
196 * Unable to get command from client! Send an error status and return.
199 syslog(LOG_ERR
, "Unable to get command line from client!");
205 * The first byte is the command byte. After that will be the queue name,
206 * resource list, and/or user name.
212 for (list
= dest
+ 1; *list
&& !isspace(*list
& 255); list
++);
214 while (isspace(*list
& 255))
223 default : /* Unknown command */
224 syslog(LOG_ERR
, "Unknown LPD command 0x%02X!", command
);
225 syslog(LOG_ERR
, "Command line = %s", line
+ 1);
231 case 0x01 : /* Print any waiting jobs */
232 syslog(LOG_INFO
, "Print waiting jobs (no-op)");
238 case 0x02 : /* Receive a printer job */
239 syslog(LOG_INFO
, "Receive print job for %s", dest
);
240 /* recv_print_job() sends initial status byte */
242 status
= recv_print_job(dest
, num_defaults
, defaults
);
245 case 0x03 : /* Send queue state (short) */
246 syslog(LOG_INFO
, "Send queue state (short) for %s %s", dest
, list
);
247 /* no status byte for this command */
249 status
= send_state(dest
, list
, 0);
252 case 0x04 : /* Send queue state (long) */
253 syslog(LOG_INFO
, "Send queue state (long) for %s %s", dest
, list
);
254 /* no status byte for this command */
256 status
= send_state(dest
, list
, 1);
259 case 0x05 : /* Remove jobs */
261 * Grab the agent and skip to the list of users and/or jobs.
266 for (; *list
&& !isspace(*list
& 255); list
++);
267 while (isspace(*list
& 255))
270 syslog(LOG_INFO
, "Remove jobs %s on %s by %s", list
, dest
, agent
);
272 status
= remove_jobs(dest
, agent
, list
);
278 syslog(LOG_INFO
, "Closing connection");
286 * 'check_printer()' - Check that a printer exists and is accepting jobs.
290 check_printer(const char *name
) /* I - Printer or class name */
292 http_t
*http
; /* Connection to server */
293 ipp_t
*request
; /* IPP request */
294 ipp_t
*response
; /* IPP response */
295 ipp_attribute_t
*attr
; /* IPP job-id attribute */
296 char uri
[HTTP_MAX_URI
]; /* Printer URI */
297 cups_lang_t
*language
; /* Language to use */
298 int accepting
; /* printer-is-accepting-jobs value */
302 * Setup a connection and request data...
305 if ((http
= httpConnectEncrypt(cupsServer(), ippPort(),
306 cupsEncryption())) == NULL
)
308 syslog(LOG_ERR
, "Unable to connect to server %s: %s", cupsServer(),
314 * Build a standard CUPS URI for the printer and fill the standard IPP
318 if ((request
= ippNew()) == NULL
)
320 syslog(LOG_ERR
, "Unable to create request: %s", strerror(errno
));
325 request
->request
.op
.operation_id
= IPP_GET_PRINTER_ATTRIBUTES
;
326 request
->request
.op
.request_id
= 1;
328 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
329 "localhost", 0, "/printers/%s", name
);
331 language
= cupsLangDefault();
333 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
334 "attributes-charset", NULL
, cupsLangEncoding(language
));
336 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
337 "attributes-natural-language", NULL
,
338 language
!= NULL
? language
->language
: "C");
340 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
343 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requested-attributes",
344 NULL
, "printer-is-accepting-jobs");
350 response
= cupsDoRequest(http
, request
, "/");
352 if (response
== NULL
)
354 syslog(LOG_ERR
, "Unable to check printer status - %s",
355 ippErrorString(cupsLastError()));
358 else if (response
->request
.status
.status_code
> IPP_OK_CONFLICT
)
360 syslog(LOG_ERR
, "Unable to check printer status - %s",
361 ippErrorString(response
->request
.status
.status_code
));
364 else if ((attr
= ippFindAttribute(response
, "printer-is-accepting-jobs",
365 IPP_TAG_BOOLEAN
)) == NULL
)
367 syslog(LOG_ERR
, "No printer-is-accepting-jobs attribute found in response from server!");
371 accepting
= attr
->values
[0].boolean
;
373 if (response
!= NULL
)
377 cupsLangFree(language
);
384 * 'print_file()' - Print a file to a printer or class.
388 print_file(const char *name
, /* I - Printer or class name */
389 const char *file
, /* I - File to print */
390 const char *title
, /* I - Title of job */
391 const char *docname
, /* I - Name of job file */
392 const char *user
, /* I - Owner of job */
393 int num_options
, /* I - Number of options */
394 cups_option_t
*options
) /* I - Options */
396 http_t
*http
; /* Connection to server */
397 ipp_t
*request
; /* IPP request */
398 ipp_t
*response
; /* IPP response */
399 ipp_attribute_t
*attr
; /* IPP job-id attribute */
400 char uri
[HTTP_MAX_URI
]; /* Printer URI */
401 cups_lang_t
*language
; /* Language to use */
402 int jobid
; /* New job ID */
406 * Setup a connection and request data...
409 if ((http
= httpConnectEncrypt(cupsServer(), ippPort(),
410 cupsEncryption())) == NULL
)
412 syslog(LOG_ERR
, "Unable to connect to server %s: %s", cupsServer(),
418 * Build a standard CUPS URI for the printer and fill the standard IPP
422 if ((request
= ippNew()) == NULL
)
424 syslog(LOG_ERR
, "Unable to create request: %s", strerror(errno
));
429 request
->request
.op
.operation_id
= IPP_PRINT_JOB
;
430 request
->request
.op
.request_id
= 1;
432 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
433 "localhost", 0, "/printers/%s", name
);
435 language
= cupsLangDefault();
437 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
438 "attributes-charset", NULL
, cupsLangEncoding(language
));
440 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
441 "attributes-natural-language", NULL
,
442 language
!= NULL
? language
->language
: "C");
444 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
447 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
451 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
, title
);
453 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "document-name", NULL
, docname
);
456 * Then add all options on the command-line...
459 cupsEncodeOptions(request
, num_options
, options
);
465 snprintf(uri
, sizeof(uri
), "/printers/%s", name
);
467 response
= cupsDoFileRequest(http
, request
, uri
, file
);
469 if (response
== NULL
)
471 syslog(LOG_ERR
, "Unable to print file - %s",
472 ippErrorString(cupsLastError()));
475 else if (response
->request
.status
.status_code
> IPP_OK_CONFLICT
)
477 syslog(LOG_ERR
, "Unable to print file - %s",
478 ippErrorString(response
->request
.status
.status_code
));
481 else if ((attr
= ippFindAttribute(response
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
483 syslog(LOG_ERR
, "No job-id attribute found in response from server!");
488 jobid
= attr
->values
[0].integer
;
490 syslog(LOG_INFO
, "Print file - job ID = %d", jobid
);
493 if (response
!= NULL
)
497 cupsLangFree(language
);
504 * 'recv_print_job()' - Receive a print job from the client.
507 int /* O - Command status */
508 recv_print_job(const char *dest
, /* I - Destination */
510 /* I - Number of default options */
511 cups_option_t
*defaults
) /* I - Default options */
513 int i
; /* Looping var */
514 int status
; /* Command status */
515 int fd
; /* Temporary file */
516 FILE *fp
; /* File pointer */
517 char filename
[1024]; /* Temporary filename */
518 int bytes
; /* Bytes received */
519 char line
[256], /* Line from file/stdin */
520 command
, /* Command from line */
521 *count
, /* Number of bytes */
522 *name
; /* Name of file */
523 const char *cupsd_job_sheets
; /* Job sheets */
524 int num_data
; /* Number of data files */
525 char control
[1024], /* Control filename */
526 data
[32][256], /* Data files */
527 temp
[32][1024]; /* Temporary files */
528 char user
[1024], /* User name */
529 title
[1024], /* Job title */
530 docname
[1024], /* Document name */
531 queue
[256], /* Printer/class queue */
532 *instance
; /* Printer/class instance */
533 int num_dests
; /* Number of destinations */
534 cups_dest_t
*dests
, /* Destinations */
535 *destptr
; /* Current destination */
536 int num_options
; /* Number of options */
537 cups_option_t
*options
; /* Options */
538 int banner
; /* Print banner? */
547 strlcpy(queue
, dest
, sizeof(queue
));
549 if ((instance
= strrchr(queue
, '/')) != NULL
)
552 num_dests
= cupsGetDests(&dests
);
553 if ((destptr
= cupsGetDest(queue
, instance
, num_dests
, dests
)) == NULL
)
556 * If the queue name is blank or "lp" then use the default queue.
559 if (!queue
[0] || !strcmp(queue
, "lp"))
560 if ((destptr
= cupsGetDest(NULL
, NULL
, num_dests
, dests
)) != NULL
)
561 strlcpy(queue
, destptr
->name
, sizeof(queue
));
566 syslog(LOG_ERR
, "Unknown destination %s/%s!", queue
, instance
);
568 syslog(LOG_ERR
, "Unknown destination %s!", queue
);
570 cupsFreeDests(num_dests
, dests
);
578 if (!check_printer(queue
))
580 cupsFreeDests(num_dests
, dests
);
589 while (smart_gets(line
, sizeof(line
), stdin
) != NULL
)
591 if (strlen(line
) < 2)
600 for (name
= count
+ 1; *name
&& !isspace(*name
& 255); name
++);
601 while (isspace(*name
& 255))
607 case 0x01 : /* Abort */
611 case 0x02 : /* Receive control file */
612 if (strlen(name
) < 2)
614 syslog(LOG_ERR
, "Bad control file name \"%s\"", name
);
623 * Append to the existing control file - the LPD spec is
624 * not entirely clear, but at least the OS/2 LPD code sends
625 * multiple control files per connection...
628 if ((fd
= open(control
, O_WRONLY
)) < 0)
631 "Unable to append to temporary control file \"%s\" - %s",
632 control
, strerror(errno
));
638 lseek(fd
, 0, SEEK_END
);
642 if ((fd
= cupsTempFd(control
, sizeof(control
))) < 0)
644 syslog(LOG_ERR
, "Unable to open temporary control file \"%s\" - %s",
645 control
, strerror(errno
));
651 strcpy(filename
, control
);
655 case 0x03 : /* Receive data file */
656 if (strlen(name
) < 2)
658 syslog(LOG_ERR
, "Bad data file name \"%s\"", name
);
664 if (num_data
>= (sizeof(data
) / sizeof(data
[0])))
667 * Too many data files...
670 syslog(LOG_ERR
, "Too many data files (%d)", num_data
);
676 strlcpy(data
[num_data
], name
, sizeof(data
[0]));
678 if ((fd
= cupsTempFd(temp
[num_data
], sizeof(temp
[0]))) < 0)
680 syslog(LOG_ERR
, "Unable to open temporary data file \"%s\" - %s",
681 temp
[num_data
], strerror(errno
));
687 strcpy(filename
, temp
[num_data
]);
699 * Copy the data or control file from the client...
702 for (i
= atoi(count
); i
> 0; i
-= bytes
)
704 if (i
> sizeof(line
))
705 bytes
= sizeof(line
);
709 if ((bytes
= fread(line
, 1, bytes
, stdin
)) > 0)
710 bytes
= write(fd
, line
, bytes
);
714 syslog(LOG_ERR
, "Error while reading file - %s",
722 * Read trailing nul...
727 if (fread(line
, 1, 1, stdin
) < 1)
730 syslog(LOG_ERR
, "Error while reading trailing nul - %s",
736 syslog(LOG_ERR
, "Trailing character after file is not nul (%02X)!",
742 * Close the file and send an acknowledgement...
756 * Process the control file and print stuff...
759 if ((fp
= fopen(control
, "rb")) == NULL
)
764 * Grab the job information first...
772 while (smart_gets(line
, sizeof(line
), fp
) != NULL
)
775 * Process control lines...
780 case 'J' : /* Job name */
781 strlcpy(title
, line
+ 1, sizeof(title
));
784 case 'N' : /* Document name */
785 strlcpy(docname
, line
+ 1, sizeof(docname
));
788 case 'P' : /* User identification */
789 strlcpy(user
, line
+ 1, sizeof(user
));
792 case 'L' : /* Print banner page */
802 * Then print the jobs...
807 while (smart_gets(line
, sizeof(line
), fp
) != NULL
)
810 * Process control lines...
815 case 'c' : /* Plot CIF file */
816 case 'd' : /* Print DVI file */
817 case 'f' : /* Print formatted file */
818 case 'g' : /* Plot file */
819 case 'l' : /* Print file leaving control characters (raw) */
820 case 'n' : /* Print ditroff output file */
821 case 'o' : /* Print PostScript output file */
822 case 'p' : /* Print file with 'pr' format (prettyprint) */
823 case 'r' : /* File to print with FORTRAN carriage control */
824 case 't' : /* Print troff output file */
825 case 'v' : /* Print raster file */
827 * Check that we have a username...
832 syslog(LOG_WARNING
, "No username specified by client! "
833 "Using \"anonymous\"...");
834 strcpy(user
, "anonymous");
838 * Copy the default options...
844 for (i
= 0; i
< destptr
->num_options
; i
++)
845 num_options
= cupsAddOption(destptr
->options
[i
].name
,
846 destptr
->options
[i
].value
,
847 num_options
, &options
);
848 for (i
= 0; i
< num_defaults
; i
++)
849 num_options
= cupsAddOption(defaults
[i
].name
,
851 num_options
, &options
);
854 * If a banner was requested and it's not overridden by a
855 * command line option and the destination's default is none
856 * then add the standard banner...
860 cupsGetOption("job-sheets", num_defaults
, defaults
) == NULL
&&
861 ((cupsd_job_sheets
= cupsGetOption("job-sheets",
862 destptr
->num_options
,
863 destptr
->options
)) == NULL
||
864 !strcmp(cupsd_job_sheets
, "none,none")))
866 num_options
= cupsAddOption("job-sheets", "standard",
867 num_options
, &options
);
871 * Add additional options as needed...
875 num_options
= cupsAddOption("raw", "", num_options
, &options
);
878 num_options
= cupsAddOption("prettyprint", "", num_options
,
882 * Figure out which file we are printing...
885 for (i
= 0; i
< num_data
; i
++)
886 if (strcmp(data
[i
], line
+ 1) == 0)
896 * Send the print request...
899 if (print_file(queue
, temp
[i
], title
, docname
, user
, num_options
,
905 cupsFreeOptions(num_options
, options
);
918 * Clean up all temporary files and return...
923 for (i
= 0; i
< num_data
; i
++)
926 cupsFreeDests(num_dests
, dests
);
933 * 'remove_jobs()' - Cancel one or more jobs.
936 int /* O - Command status */
937 remove_jobs(const char *dest
, /* I - Destination */
938 const char *agent
, /* I - User agent */
939 const char *list
) /* I - List of jobs or users */
942 http_t
*http
; /* HTTP server connection */
943 ipp_t
*request
, /* IPP Request */
944 *response
; /* IPP Response */
945 cups_lang_t
*language
; /* Default language */
946 char uri
[HTTP_MAX_URI
]; /* Job URI */
949 (void)dest
; /* Suppress compiler warnings... */
952 * Try connecting to the local server...
955 if ((http
= httpConnectEncrypt(cupsServer(), ippPort(),
956 cupsEncryption())) == NULL
)
958 syslog(LOG_ERR
, "Unable to connect to server %s: %s", cupsServer(),
963 language
= cupsLangDefault();
966 * Loop for each job...
969 while ((id
= atoi(list
)) > 0)
972 * Skip job ID in list...
975 while (isdigit(*list
& 255))
977 while (isspace(*list
& 255))
981 * Build an IPP_CANCEL_JOB request, which requires the following
985 * attributes-natural-language
987 * requesting-user-name
992 request
->request
.op
.operation_id
= IPP_CANCEL_JOB
;
993 request
->request
.op
.request_id
= 1;
995 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
996 "attributes-charset", NULL
, cupsLangEncoding(language
));
998 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
999 "attributes-natural-language", NULL
, language
->language
);
1001 sprintf(uri
, "ipp://localhost/jobs/%d", id
);
1002 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
1004 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1005 "requesting-user-name", NULL
, agent
);
1008 * Do the request and get back a response...
1011 if ((response
= cupsDoRequest(http
, request
, "/jobs")) != NULL
)
1013 if (response
->request
.status
.status_code
> IPP_OK_CONFLICT
)
1015 syslog(LOG_WARNING
, "Cancel of job ID %d failed: %s\n", id
,
1016 ippErrorString(response
->request
.status
.status_code
));
1017 ippDelete(response
);
1018 cupsLangFree(language
);
1023 syslog(LOG_INFO
, "Job ID %d cancelled", id
);
1025 ippDelete(response
);
1029 syslog(LOG_WARNING
, "Cancel of job ID %d failed: %s\n", id
,
1030 ippErrorString(cupsLastError()));
1031 cupsLangFree(language
);
1037 cupsLangFree(language
);
1045 * 'send_state()' - Send the queue state.
1048 int /* O - Command status */
1049 send_state(const char *dest
, /* I - Destination */
1050 const char *list
, /* I - Job or user */
1051 int longstatus
) /* I - List of jobs or users */
1053 int id
; /* Job ID from list */
1054 http_t
*http
; /* HTTP server connection */
1055 ipp_t
*request
, /* IPP Request */
1056 *response
; /* IPP Response */
1057 ipp_attribute_t
*attr
; /* Current attribute */
1058 cups_lang_t
*language
; /* Default language */
1059 ipp_pstate_t state
; /* Printer state */
1060 const char *jobdest
, /* Pointer into job-printer-uri */
1061 *jobuser
, /* Pointer to job-originating-user-name */
1062 *jobname
; /* Pointer to job-name */
1063 ipp_jstate_t jobstate
; /* job-state */
1064 int jobid
, /* job-id */
1065 jobsize
, /* job-k-octets */
1066 jobcount
, /* Number of jobs */
1067 jobcopies
, /* Number of copies */
1068 rank
; /* Rank of job */
1069 char rankstr
[255]; /* Rank string */
1070 char namestr
[1024]; /* Job name string */
1071 char uri
[HTTP_MAX_URI
]; /* Printer URI */
1072 char queue
[256], /* Printer/class queue */
1073 *instance
; /* Printer/class instance */
1074 static const char * const ranks
[10] = /* Ranking strings */
1087 static const char * const requested
[] =
1088 { /* Requested attributes */
1093 "job-originating-user-name",
1100 * Remove instance from destination, if any...
1103 strlcpy(queue
, dest
, sizeof(queue
));
1105 if ((instance
= strrchr(queue
, '/')) != NULL
)
1109 * Try connecting to the local server...
1112 if ((http
= httpConnectEncrypt(cupsServer(), ippPort(),
1113 cupsEncryption())) == NULL
)
1115 syslog(LOG_ERR
, "Unable to connect to server %s: %s", cupsServer(),
1117 printf("Unable to connect to server %s: %s", cupsServer(), strerror(errno
));
1122 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
1125 * attributes-charset
1126 * attributes-natural-language
1132 request
->request
.op
.operation_id
= IPP_GET_PRINTER_ATTRIBUTES
;
1133 request
->request
.op
.request_id
= 1;
1135 language
= cupsLangDefault();
1137 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1138 "attributes-charset", NULL
, cupsLangEncoding(language
));
1140 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1141 "attributes-natural-language", NULL
, language
->language
);
1143 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1144 "localhost", 0, "/printers/%s", queue
);
1145 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
1146 "printer-uri", NULL
, uri
);
1148 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1149 "requested-attributes", NULL
, "printer-state");
1152 * Do the request and get back a response...
1155 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
1157 if (response
->request
.status
.status_code
> IPP_OK_CONFLICT
)
1159 syslog(LOG_WARNING
, "Unable to get printer list: %s\n",
1160 ippErrorString(response
->request
.status
.status_code
));
1161 printf("Unable to get printer list: %s\n",
1162 ippErrorString(response
->request
.status
.status_code
));
1163 ippDelete(response
);
1167 if ((attr
= ippFindAttribute(response
, "printer-state", IPP_TAG_ENUM
)) != NULL
)
1168 state
= (ipp_pstate_t
)attr
->values
[0].integer
;
1170 state
= IPP_PRINTER_STOPPED
;
1174 case IPP_PRINTER_IDLE
:
1175 printf("%s is ready\n", dest
);
1177 case IPP_PRINTER_PROCESSING
:
1178 printf("%s is ready and printing\n", dest
);
1180 case IPP_PRINTER_STOPPED
:
1181 printf("%s is not ready\n", dest
);
1185 ippDelete(response
);
1189 syslog(LOG_WARNING
, "Unable to get printer list: %s\n",
1190 ippErrorString(cupsLastError()));
1191 printf("Unable to get printer list: %s\n",
1192 ippErrorString(cupsLastError()));
1197 * Build an IPP_GET_JOBS or IPP_GET_JOB_ATTRIBUTES request, which requires
1198 * the following attributes:
1200 * attributes-charset
1201 * attributes-natural-language
1202 * job-uri or printer-uri
1209 request
->request
.op
.operation_id
= id
? IPP_GET_JOB_ATTRIBUTES
: IPP_GET_JOBS
;
1210 request
->request
.op
.request_id
= 1;
1212 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1213 "attributes-charset", NULL
, cupsLangEncoding(language
));
1215 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1216 "attributes-natural-language", NULL
, language
->language
);
1218 httpAssembleURIf(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1219 "localhost", 0, "/printers/%s", queue
);
1221 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1225 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", id
);
1228 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1229 "requesting-user-name", NULL
, list
);
1230 ippAddBoolean(request
, IPP_TAG_OPERATION
, "my-jobs", 1);
1233 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1234 "requested-attributes", sizeof(requested
) / sizeof(requested
[0]),
1238 * Do the request and get back a response...
1243 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
1245 if (response
->request
.status
.status_code
> IPP_OK_CONFLICT
)
1247 printf("get-jobs failed: %s\n",
1248 ippErrorString(response
->request
.status
.status_code
));
1249 ippDelete(response
);
1256 * Loop through the job list and display them...
1259 for (attr
= response
->attrs
; attr
!= NULL
; attr
= attr
->next
)
1262 * Skip leading attributes until we hit a job...
1265 while (attr
!= NULL
&&
1266 (attr
->group_tag
!= IPP_TAG_JOB
|| attr
->name
== NULL
))
1273 * Pull the needed attributes from this job...
1278 jobstate
= IPP_JOB_PENDING
;
1279 jobname
= "untitled";
1284 while (attr
!= NULL
&& attr
->group_tag
== IPP_TAG_JOB
)
1286 if (strcmp(attr
->name
, "job-id") == 0 &&
1287 attr
->value_tag
== IPP_TAG_INTEGER
)
1288 jobid
= attr
->values
[0].integer
;
1290 if (strcmp(attr
->name
, "job-k-octets") == 0 &&
1291 attr
->value_tag
== IPP_TAG_INTEGER
)
1292 jobsize
= attr
->values
[0].integer
;
1294 if (strcmp(attr
->name
, "job-state") == 0 &&
1295 attr
->value_tag
== IPP_TAG_ENUM
)
1296 jobstate
= (ipp_jstate_t
)attr
->values
[0].integer
;
1298 if (strcmp(attr
->name
, "job-printer-uri") == 0 &&
1299 attr
->value_tag
== IPP_TAG_URI
)
1300 if ((jobdest
= strrchr(attr
->values
[0].string
.text
, '/')) != NULL
)
1303 if (strcmp(attr
->name
, "job-originating-user-name") == 0 &&
1304 attr
->value_tag
== IPP_TAG_NAME
)
1305 jobuser
= attr
->values
[0].string
.text
;
1307 if (strcmp(attr
->name
, "job-name") == 0 &&
1308 attr
->value_tag
== IPP_TAG_NAME
)
1309 jobname
= attr
->values
[0].string
.text
;
1311 if (strcmp(attr
->name
, "copies") == 0 &&
1312 attr
->value_tag
== IPP_TAG_INTEGER
)
1313 jobcopies
= attr
->values
[0].integer
;
1319 * See if we have everything needed...
1322 if (jobdest
== NULL
|| jobid
== 0)
1330 if (!longstatus
&& jobcount
== 0)
1331 puts("Rank Owner Job File(s) Total Size");
1336 * Display the job...
1339 if (jobstate
== IPP_JOB_PROCESSING
)
1340 strcpy(rankstr
, "active");
1343 snprintf(rankstr
, sizeof(rankstr
), "%d%s", rank
, ranks
[rank
% 10]);
1352 snprintf(namestr
, sizeof(namestr
), "%d copies of %s", jobcopies
,
1355 strlcpy(namestr
, jobname
, sizeof(namestr
));
1357 printf("%s: %-33.33s [job %d localhost]\n", jobuser
, rankstr
, jobid
);
1358 printf(" %-39.39s %.0f bytes\n", namestr
, 1024.0 * jobsize
);
1361 printf("%-7s %-7.7s %-7d %-31.31s %.0f bytes\n", rankstr
, jobuser
,
1362 jobid
, jobname
, 1024.0 * jobsize
);
1368 ippDelete(response
);
1372 printf("get-jobs failed: %s\n", ippErrorString(cupsLastError()));
1379 cupsLangFree(language
);
1387 * 'smart_gets()' - Get a line of text, removing the trailing CR and/or LF.
1390 char * /* O - Line read or NULL */
1391 smart_gets(char *s
, /* I - Pointer to line buffer */
1392 int len
, /* I - Size of line buffer */
1393 FILE *fp
) /* I - File to read from */
1395 char *ptr
, /* Pointer into line */
1396 *end
; /* End of line */
1397 int ch
; /* Character from file */
1401 * Read the line; unlike fgets(), we read the entire line but dump
1402 * characters that go past the end of the buffer. Also, we accept
1403 * CR, LF, or CR LF for the line endings to be "safe", although
1404 * RFC 1179 specifically says "just use LF".
1410 while ((ch
= getc(fp
)) != EOF
)
1414 else if (ch
== '\r')
1417 * See if a LF follows...
1433 if (ch
== EOF
&& ptr
== s
)
1441 * End of "$Id: cups-lpd.c 5049 2006-02-02 14:50:57Z mike $".