2 * "$Id: cups-lpd.c,v 1.32 2002/04/29 17:25:26 mike Exp $"
4 * Line Printer Daemon interface for the Common UNIX Printing System (CUPS).
6 * Copyright 1997-2002 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-3111 USA
20 * Voice: (301) 373-9603
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_short_state() - Send the short queue state.
31 * smart_gets() - Get a line of text, removing the trailing CR
36 * Include necessary headers...
39 #include <cups/cups.h>
40 #include <cups/string.h>
41 #include <cups/language.h>
49 #include <sys/types.h>
50 #include <sys/socket.h>
51 #include <netinet/in.h>
56 * LPD "mini-daemon" for CUPS. This program must be used in conjunction
57 * with inetd or another similar program that monitors ports and starts
58 * daemons for each client connection. A typical configuration is:
60 * printer stream tcp nowait lp /usr/lib/cups/daemon/cups-lpd cups-lpd
62 * This daemon implements most of RFC 1179 (the unofficial LPD specification)
65 * - This daemon does not check to make sure that the source port is
66 * between 721 and 731, since it isn't necessary for proper
67 * functioning and port-based security is no security at all!
69 * - The "Print any waiting jobs" command is a no-op.
71 * The LPD-to-IPP mapping is as defined in RFC 2569. The report formats
72 * currently match the Solaris LPD mini-daemon.
79 int print_file(const char *name
, const char *file
,
80 const char *title
, const char *docname
,
81 const char *user
, int num_options
,
82 cups_option_t
*options
);
83 int recv_print_job(const char *dest
, int num_defaults
, cups_option_t
*defaults
);
84 int remove_jobs(const char *dest
, const char *agent
, const char *list
);
85 int send_state(const char *dest
, const char *list
, int longstatus
);
86 char *smart_gets(char *s
, int len
, FILE *fp
);
90 * 'main()' - Process an incoming LPD request...
93 int /* O - Exit status */
94 main(int argc
, /* I - Number of command-line arguments */
95 char *argv
[]) /* I - Command-line arguments */
97 int i
; /* Looping var */
98 int num_defaults
; /* Number of default options */
99 cups_option_t
*defaults
; /* Default options */
100 char line
[256], /* Command string */
101 command
, /* Command code */
102 *dest
, /* Pointer to destination */
103 *list
, /* Pointer to list */
104 *agent
, /* Pointer to user */
105 status
; /* Status for client */
106 int hostlen
; /* Size of client address */
107 unsigned hostip
; /* (32-bit) IP address */
108 struct sockaddr_in hostaddr
; /* Address of client */
109 struct hostent
*hostent
; /* Host entry of client */
110 char hostname
[256]; /* Hostname of client */
114 * Don't buffer the output...
117 setbuf(stdout
, NULL
);
120 * Log things using the "cups-lpd" name...
123 openlog("cups-lpd", LOG_PID
, LOG_LPR
);
126 * Get the address of the client...
129 hostlen
= sizeof(hostaddr
);
131 if (getpeername(0, (struct sockaddr
*)&hostaddr
, &hostlen
))
133 syslog(LOG_WARNING
, "Unable to get client address - %s", strerror(errno
));
134 strcpy(hostname
, "unknown");
138 hostip
= ntohl(hostaddr
.sin_addr
.s_addr
);
139 hostent
= gethostbyaddr((void *)&(hostaddr
.sin_addr
), hostlen
, AF_INET
);
143 strncpy(hostname
, hostent
->h_name
, sizeof(hostname
) - 1);
144 hostname
[sizeof(hostname
) - 1] = '\0';
148 snprintf(hostname
, sizeof(hostname
), "%d.%d.%d.%d",
149 (hostip
>> 24) & 255, (hostip
>> 16) & 255,
150 (hostip
>> 8) & 255, hostip
& 255);
153 syslog(LOG_INFO
, "Connection from %s (%d.%d.%d.%d)",
154 hostname
, (hostip
>> 24) & 255, (hostip
>> 16) & 255,
155 (hostip
>> 8) & 255, hostip
& 255);
159 * Scan the command-line for options...
165 num_defaults
= cupsAddOption("job-originating-host-name", hostname
,
166 num_defaults
, &defaults
);
168 for (i
= 1; i
< argc
; i
++)
169 if (argv
[i
][0] == '-')
173 case 'o' : /* Option */
175 num_defaults
= cupsParseOptions(argv
[i
] + 2, num_defaults
,
181 num_defaults
= cupsParseOptions(argv
[i
], num_defaults
, &defaults
);
183 syslog(LOG_WARNING
, "Expected option string after -o option!");
187 syslog(LOG_WARNING
, "Unknown option \"%c\" ignored!", argv
[i
][1]);
192 syslog(LOG_WARNING
, "Unknown command-line option \"%s\" ignored!", argv
[i
]);
195 * RFC1179 specifies that only 1 daemon command can be received for
199 if (smart_gets(line
, sizeof(line
), stdin
) == NULL
)
202 * Unable to get command from client! Send an error status and return.
205 syslog(LOG_ERR
, "Unable to get command line from client!");
211 * The first byte is the command byte. After that will be the queue name,
212 * resource list, and/or user name.
218 for (list
= dest
+ 1; *list
&& !isspace(*list
); list
++);
220 while (isspace(*list
))
229 default : /* Unknown command */
230 syslog(LOG_ERR
, "Unknown LPD command 0x%02X!", command
);
231 syslog(LOG_ERR
, "Command line = %s", line
+ 1);
237 case 0x01 : /* Print any waiting jobs */
238 syslog(LOG_INFO
, "Print waiting jobs (no-op)");
244 case 0x02 : /* Receive a printer job */
245 syslog(LOG_INFO
, "Receive print job for %s", dest
);
246 /* recv_print_job() sends initial status byte */
248 status
= recv_print_job(dest
, num_defaults
, defaults
);
251 case 0x03 : /* Send queue state (short) */
252 syslog(LOG_INFO
, "Send queue state (short) for %s %s", dest
, list
);
253 /* send_state() sends initial status byte */
255 status
= send_state(dest
, list
, 0);
258 case 0x04 : /* Send queue state (long) */
259 syslog(LOG_INFO
, "Send queue state (long) for %s %s", dest
, list
);
260 /* send_state() sends initial status byte */
262 status
= send_state(dest
, list
, 1);
265 case 0x05 : /* Remove jobs */
267 * Grab the agent and skip to the list of users and/or jobs.
272 for (; *list
&& !isspace(*list
); list
++);
273 while (isspace(*list
))
276 syslog(LOG_INFO
, "Remove jobs %s on %s by %s", list
, dest
, agent
);
278 status
= remove_jobs(dest
, agent
, list
);
284 syslog(LOG_INFO
, "Closing connection");
292 * 'print_file()' - Print a file to a printer or class.
296 print_file(const char *name
, /* I - Printer or class name */
297 const char *file
, /* I - File to print */
298 const char *title
, /* I - Title of job */
299 const char *docname
, /* I - Name of job file */
300 const char *user
, /* I - Owner of job */
301 int num_options
, /* I - Number of options */
302 cups_option_t
*options
) /* I - Options */
304 http_t
*http
; /* Connection to server */
305 ipp_t
*request
; /* IPP request */
306 ipp_t
*response
; /* IPP response */
307 ipp_attribute_t
*attr
; /* IPP job-id attribute */
308 char uri
[HTTP_MAX_URI
]; /* Printer URI */
309 cups_lang_t
*language
; /* Language to use */
310 int jobid
; /* New job ID */
314 * Setup a connection and request data...
317 if ((http
= httpConnectEncrypt(cupsServer(), ippPort(),
318 cupsEncryption())) == NULL
)
320 syslog(LOG_ERR
, "Unable to connect to server: %s", strerror(errno
));
324 language
= cupsLangDefault();
327 * Build a standard CUPS URI for the printer and fill the standard IPP
331 if ((request
= ippNew()) == NULL
)
333 syslog(LOG_ERR
, "Unable to create request: %s", strerror(errno
));
337 request
->request
.op
.operation_id
= IPP_PRINT_JOB
;
338 request
->request
.op
.request_id
= 1;
340 snprintf(uri
, sizeof(uri
), "ipp://localhost/printers/%s", name
);
342 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
343 "attributes-charset", NULL
, cupsLangEncoding(language
));
345 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
346 "attributes-natural-language", NULL
,
347 language
!= NULL
? language
->language
: "C");
349 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
352 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "requesting-user-name",
356 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "job-name", NULL
, title
);
358 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
, "document-name", NULL
, docname
);
361 * Then add all options on the command-line...
364 cupsEncodeOptions(request
, num_options
, options
);
370 snprintf(uri
, sizeof(uri
), "/printers/%s", name
);
372 response
= cupsDoFileRequest(http
, request
, uri
, file
);
374 if (response
== NULL
)
376 else if (response
->request
.status
.status_code
> IPP_OK_CONFLICT
)
378 else if ((attr
= ippFindAttribute(response
, "job-id", IPP_TAG_INTEGER
)) == NULL
)
381 jobid
= attr
->values
[0].integer
;
384 syslog(LOG_INFO
, "Print file - job ID = %d", jobid
);
386 syslog(LOG_ERR
, "Unable to print file - %s",
387 ippErrorString(response
->request
.status
.status_code
));
389 syslog(LOG_ERR
, "Unable to print file - %s",
390 ippErrorString(cupsLastError()));
392 if (response
!= NULL
)
396 cupsLangFree(language
);
403 * 'recv_print_job()' - Receive a print job from the client.
406 int /* O - Command status */
407 recv_print_job(const char *dest
, /* I - Destination */
408 int num_defaults
,/* I - Number of default options */
409 cups_option_t
*defaults
) /* I - Default options */
411 int i
; /* Looping var */
412 int status
; /* Command status */
413 int fd
; /* Temporary file */
414 FILE *fp
; /* File pointer */
415 char filename
[1024]; /* Temporary filename */
416 int bytes
; /* Bytes received */
417 char line
[256], /* Line from file/stdin */
418 command
, /* Command from line */
419 *count
, /* Number of bytes */
420 *name
; /* Name of file */
421 int num_data
; /* Number of data files */
422 char control
[1024], /* Control filename */
423 data
[32][256], /* Data files */
424 temp
[32][1024]; /* Temporary files */
425 char user
[1024], /* User name */
426 title
[1024], /* Job title */
427 docname
[1024], /* Document name */
428 queue
[256], /* Printer/class queue */
429 *instance
; /* Printer/class instance */
430 int num_dests
; /* Number of destinations */
431 cups_dest_t
*dests
, /* Destinations */
432 *destptr
; /* Current destination */
433 int num_options
; /* Number of options */
434 cups_option_t
*options
; /* Options */
435 int banner
; /* Print banner? */
444 strncpy(queue
, dest
, sizeof(queue
) - 1);
445 queue
[sizeof(queue
) - 1] = '\0';
447 if ((instance
= strrchr(queue
, '/')) != NULL
)
450 num_dests
= cupsGetDests(&dests
);
451 if ((destptr
= cupsGetDest(queue
, instance
, num_dests
, dests
)) == NULL
)
454 syslog(LOG_ERR
, "Unknown destination %s/%s!", queue
, instance
);
456 syslog(LOG_ERR
, "Unknown destination %s!", queue
);
458 cupsFreeDests(num_dests
, dests
);
467 while (smart_gets(line
, sizeof(line
), stdin
) != NULL
)
469 if (strlen(line
) < 2)
478 for (name
= count
+ 1; *name
&& !isspace(*name
); name
++);
479 while (isspace(*name
))
485 case 0x01 : /* Abort */
488 case 0x02 : /* Receive control file */
489 if (strlen(name
) < 2)
491 syslog(LOG_ERR
, "Bad control file name \"%s\"", name
);
500 * Append to the existing control file - the LPD spec is
501 * not entirely clear, but at least the OS/2 LPD code sends
502 * multiple control files per connection...
505 if ((fd
= open(control
, O_WRONLY
)) < 0)
507 syslog(LOG_ERR
, "Unable to append to temporary control file - %s",
514 lseek(fd
, 0, SEEK_END
);
518 if ((fd
= cupsTempFd(control
, sizeof(control
))) < 0)
520 syslog(LOG_ERR
, "Unable to open temporary control file - %s",
527 strcpy(filename
, control
);
530 case 0x03 : /* Receive data file */
531 if (strlen(name
) < 2)
533 syslog(LOG_ERR
, "Bad data file name \"%s\"", name
);
539 if (num_data
>= (sizeof(data
) / sizeof(data
[0])))
542 * Too many data files...
545 syslog(LOG_ERR
, "Too many data files (%d)", num_data
);
551 strncpy(data
[num_data
], name
, sizeof(data
[0]) - 1);
552 data
[num_data
][sizeof(data
[0]) - 1] = '\0';
554 if ((fd
= cupsTempFd(temp
[num_data
], sizeof(temp
[0]))) < 0)
556 syslog(LOG_ERR
, "Unable to open temporary data file - %s",
563 strcpy(filename
, temp
[num_data
]);
575 * Copy the data or control file from the client...
578 for (i
= atoi(count
); i
> 0; i
-= bytes
)
580 if (i
> sizeof(line
))
581 bytes
= sizeof(line
);
585 if ((bytes
= fread(line
, 1, bytes
, stdin
)) > 0)
586 bytes
= write(fd
, line
, bytes
);
590 syslog(LOG_ERR
, "Error while reading file - %s",
598 * Read trailing nul...
603 if (fread(line
, 1, 1, stdin
) < 1)
606 syslog(LOG_ERR
, "Error while reading trailing nul - %s",
612 syslog(LOG_ERR
, "Trailing character after file is not nul (%02X)!",
618 * Close the file and send an acknowledgement...
632 * Process the control file and print stuff...
635 if ((fp
= fopen(control
, "rb")) == NULL
)
640 * Grab the job information first...
648 while (smart_gets(line
, sizeof(line
), fp
) != NULL
)
651 * Process control lines...
656 case 'J' : /* Job name */
657 strncpy(title
, line
+ 1, sizeof(title
) - 1);
658 title
[sizeof(title
) - 1] = '\0';
660 case 'N' : /* Document name */
661 strncpy(docname
, line
+ 1, sizeof(docname
) - 1);
662 docname
[sizeof(docname
) - 1] = '\0';
664 case 'P' : /* User identification */
665 strncpy(user
, line
+ 1, sizeof(user
) - 1);
666 user
[sizeof(user
) - 1] = '\0';
668 case 'L' : /* Print banner page */
678 * Then print the jobs...
683 while (smart_gets(line
, sizeof(line
), fp
) != NULL
)
686 * Process control lines...
691 case 'c' : /* Plot CIF file */
692 case 'd' : /* Print DVI file */
693 case 'f' : /* Print formatted file */
694 case 'g' : /* Plot file */
695 case 'l' : /* Print file leaving control characters (raw) */
696 case 'n' : /* Print ditroff output file */
697 case 'o' : /* Print PostScript output file */
698 case 'p' : /* Print file with 'pr' format (prettyprint) */
699 case 'r' : /* File to print with FORTRAN carriage control */
700 case 't' : /* Print troff output file */
701 case 'v' : /* Print raster file */
703 * Check that we have a username...
708 syslog(LOG_WARNING
, "No username specified by client! "
709 "Using \"anonymous\"...");
710 strcpy(user
, "anonymous");
714 * Copy the default options...
720 for (i
= 0; i
< num_defaults
; i
++)
721 num_options
= cupsAddOption(defaults
[i
].name
,
723 num_options
, &options
);
724 for (i
= 0; i
< destptr
->num_options
; i
++)
725 num_options
= cupsAddOption(destptr
->options
[i
].name
,
726 destptr
->options
[i
].value
,
727 num_options
, &options
);
730 * Add additional options as needed...
734 num_options
= cupsAddOption("job-sheets", "none",
735 num_options
, &options
);
738 num_options
= cupsAddOption("raw", "", num_options
, &options
);
741 num_options
= cupsAddOption("prettyprint", "", num_options
,
745 * Figure out which file we are printing...
748 for (i
= 0; i
< num_data
; i
++)
749 if (strcmp(data
[i
], line
+ 1) == 0)
759 * Send the print request...
762 if (print_file(queue
, temp
[i
], title
, docname
, user
, num_options
,
768 cupsFreeOptions(num_options
, options
);
781 * Clean up all temporary files and return...
786 for (i
= 0; i
< num_data
; i
++)
789 cupsFreeDests(num_dests
, dests
);
796 * 'remove_jobs()' - Cancel one or more jobs.
799 int /* O - Command status */
800 remove_jobs(const char *dest
, /* I - Destination */
801 const char *agent
, /* I - User agent */
802 const char *list
) /* I - List of jobs or users */
805 http_t
*http
; /* HTTP server connection */
806 ipp_t
*request
, /* IPP Request */
807 *response
; /* IPP Response */
808 cups_lang_t
*language
; /* Default language */
809 char uri
[HTTP_MAX_URI
]; /* Job URI */
812 (void)dest
; /* Suppress compiler warnings... */
815 * Try connecting to the local server...
818 if ((http
= httpConnectEncrypt(cupsServer(), ippPort(),
819 cupsEncryption())) == NULL
)
821 syslog(LOG_ERR
, "Unable to connect to server: %s", strerror(errno
));
825 language
= cupsLangDefault();
828 * Loop for each job...
831 while ((id
= atoi(list
)) > 0)
834 * Skip job ID in list...
837 while (isdigit(*list
))
839 while (isspace(*list
))
843 * Build an IPP_CANCEL_JOB request, which requires the following
847 * attributes-natural-language
849 * requesting-user-name
854 request
->request
.op
.operation_id
= IPP_CANCEL_JOB
;
855 request
->request
.op
.request_id
= 1;
857 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
858 "attributes-charset", NULL
, cupsLangEncoding(language
));
860 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
861 "attributes-natural-language", NULL
, language
->language
);
863 sprintf(uri
, "ipp://localhost/jobs/%d", id
);
864 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "job-uri", NULL
, uri
);
866 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
867 "requesting-user-name", NULL
, agent
);
870 * Do the request and get back a response...
873 if ((response
= cupsDoRequest(http
, request
, "/jobs")) != NULL
)
875 if (response
->request
.status
.status_code
> IPP_OK_CONFLICT
)
877 syslog(LOG_WARNING
, "Cancel of job ID %d failed: %s\n", id
,
878 ippErrorString(response
->request
.status
.status_code
));
880 cupsLangFree(language
);
885 syslog(LOG_INFO
, "Job ID %d cancelled", id
);
891 syslog(LOG_WARNING
, "Cancel of job ID %d failed: %s\n", id
,
892 ippErrorString(cupsLastError()));
893 cupsLangFree(language
);
899 cupsLangFree(language
);
907 * 'send_state()' - Send the queue state.
910 int /* O - Command status */
911 send_state(const char *dest
, /* I - Destination */
912 const char *list
, /* I - Job or user */
913 int longstatus
) /* I - List of jobs or users */
915 int id
; /* Job ID from list */
916 http_t
*http
; /* HTTP server connection */
917 ipp_t
*request
, /* IPP Request */
918 *response
; /* IPP Response */
919 ipp_attribute_t
*attr
; /* Current attribute */
920 cups_lang_t
*language
; /* Default language */
921 ipp_pstate_t state
; /* Printer state */
922 const char *jobdest
, /* Pointer into job-printer-uri */
923 *jobuser
, /* Pointer to job-originating-user-name */
924 *jobname
; /* Pointer to job-name */
925 ipp_jstate_t jobstate
; /* job-state */
926 int jobid
, /* job-id */
927 jobsize
, /* job-k-octets */
928 jobcount
, /* Number of jobs */
929 jobcopies
, /* Number of copies */
930 rank
; /* Rank of job */
931 char rankstr
[255]; /* Rank string */
932 char namestr
[1024]; /* Job name string */
933 char uri
[HTTP_MAX_URI
]; /* Printer URI */
934 char queue
[256], /* Printer/class queue */
935 *instance
; /* Printer/class instance */
936 static const char *ranks
[10] = /* Ranking strings */
949 static const char *requested
[] = /* Requested attributes */
955 "job-originating-user-name",
962 * Remove instance from destination, if any...
965 strncpy(queue
, dest
, sizeof(queue
) - 1);
966 queue
[sizeof(queue
) - 1] = '\0';
968 if ((instance
= strrchr(queue
, '/')) != NULL
)
972 * Try connecting to the local server...
975 if ((http
= httpConnectEncrypt(cupsServer(), ippPort(),
976 cupsEncryption())) == NULL
)
978 syslog(LOG_ERR
, "Unable to connect to server: %s", strerror(errno
));
984 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
988 * attributes-natural-language
994 request
->request
.op
.operation_id
= IPP_GET_PRINTER_ATTRIBUTES
;
995 request
->request
.op
.request_id
= 1;
997 language
= cupsLangDefault();
999 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1000 "attributes-charset", NULL
, cupsLangEncoding(language
));
1002 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1003 "attributes-natural-language", NULL
, language
->language
);
1005 snprintf(uri
, sizeof(uri
), "ipp://localhost/printers/%s", queue
);
1006 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
,
1007 "printer-uri", NULL
, uri
);
1009 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1010 "requested-attributes", NULL
, "printer-state");
1013 * Do the request and get back a response...
1016 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
1018 if (response
->request
.status
.status_code
> IPP_OK_CONFLICT
)
1020 syslog(LOG_WARNING
, "Unable to get printer list: %s\n",
1021 ippErrorString(response
->request
.status
.status_code
));
1022 ippDelete(response
);
1029 if ((attr
= ippFindAttribute(response
, "printer-state", IPP_TAG_ENUM
)) != NULL
)
1030 state
= (ipp_pstate_t
)attr
->values
[0].integer
;
1032 state
= IPP_PRINTER_STOPPED
;
1036 case IPP_PRINTER_IDLE
:
1037 printf("%s is ready\n", dest
);
1039 case IPP_PRINTER_PROCESSING
:
1040 printf("%s is ready and printing\n", dest
);
1042 case IPP_PRINTER_STOPPED
:
1043 printf("%s is not ready\n", dest
);
1047 ippDelete(response
);
1051 syslog(LOG_WARNING
, "Unable to get printer list: %s\n",
1052 ippErrorString(cupsLastError()));
1058 * Build an IPP_GET_JOBS or IPP_GET_JOB_ATTRIBUTES request, which requires
1059 * the following attributes:
1061 * attributes-charset
1062 * attributes-natural-language
1063 * job-uri or printer-uri
1070 request
->request
.op
.operation_id
= id
? IPP_GET_JOB_ATTRIBUTES
: IPP_GET_JOBS
;
1071 request
->request
.op
.request_id
= 1;
1073 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_CHARSET
,
1074 "attributes-charset", NULL
, cupsLangEncoding(language
));
1076 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_LANGUAGE
,
1077 "attributes-natural-language", NULL
, language
->language
);
1079 snprintf(uri
, sizeof(uri
), "ipp://localhost/printers/%s", queue
);
1081 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1085 ippAddInteger(request
, IPP_TAG_OPERATION
, IPP_TAG_INTEGER
, "job-id", id
);
1088 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_NAME
,
1089 "requesting-user-name", NULL
, list
);
1090 ippAddBoolean(request
, IPP_TAG_OPERATION
, "my-jobs", 1);
1093 ippAddStrings(request
, IPP_TAG_OPERATION
, IPP_TAG_KEYWORD
,
1094 "requested-attributes", sizeof(requested
) / sizeof(requested
[0]),
1098 * Do the request and get back a response...
1103 if ((response
= cupsDoRequest(http
, request
, "/")) != NULL
)
1105 if (response
->request
.status
.status_code
> IPP_OK_CONFLICT
)
1107 printf("get-jobs failed: %s\n",
1108 ippErrorString(response
->request
.status
.status_code
));
1109 ippDelete(response
);
1116 * Loop through the job list and display them...
1119 for (attr
= response
->attrs
; attr
!= NULL
; attr
= attr
->next
)
1122 * Skip leading attributes until we hit a job...
1125 while (attr
!= NULL
&&
1126 (attr
->group_tag
!= IPP_TAG_JOB
|| attr
->name
== NULL
))
1133 * Pull the needed attributes from this job...
1138 jobstate
= IPP_JOB_PENDING
;
1139 jobname
= "untitled";
1144 while (attr
!= NULL
&& attr
->group_tag
== IPP_TAG_JOB
)
1146 if (strcmp(attr
->name
, "job-id") == 0 &&
1147 attr
->value_tag
== IPP_TAG_INTEGER
)
1148 jobid
= attr
->values
[0].integer
;
1150 if (strcmp(attr
->name
, "job-k-octets") == 0 &&
1151 attr
->value_tag
== IPP_TAG_INTEGER
)
1152 jobsize
= attr
->values
[0].integer
* 1024;
1154 if (strcmp(attr
->name
, "job-state") == 0 &&
1155 attr
->value_tag
== IPP_TAG_ENUM
)
1156 jobstate
= (ipp_jstate_t
)attr
->values
[0].integer
;
1158 if (strcmp(attr
->name
, "job-printer-uri") == 0 &&
1159 attr
->value_tag
== IPP_TAG_URI
)
1160 if ((jobdest
= strrchr(attr
->values
[0].string
.text
, '/')) != NULL
)
1163 if (strcmp(attr
->name
, "job-originating-user-name") == 0 &&
1164 attr
->value_tag
== IPP_TAG_NAME
)
1165 jobuser
= attr
->values
[0].string
.text
;
1167 if (strcmp(attr
->name
, "job-name") == 0 &&
1168 attr
->value_tag
== IPP_TAG_NAME
)
1169 jobname
= attr
->values
[0].string
.text
;
1171 if (strcmp(attr
->name
, "copies") == 0 &&
1172 attr
->value_tag
== IPP_TAG_INTEGER
)
1173 jobcopies
= attr
->values
[0].integer
;
1179 * See if we have everything needed...
1182 if (jobdest
== NULL
|| jobid
== 0)
1190 if (!longstatus
&& jobcount
== 0)
1191 puts("Rank Owner Job File(s) Total Size");
1196 * Display the job...
1199 if (jobstate
== IPP_JOB_PROCESSING
)
1200 strcpy(rankstr
, "active");
1203 snprintf(rankstr
, sizeof(rankstr
), "%d%s", rank
, ranks
[rank
% 10]);
1212 snprintf(namestr
, sizeof(namestr
), "%d copies of %s", jobcopies
,
1216 strncpy(namestr
, jobname
, sizeof(namestr
) - 1);
1217 namestr
[sizeof(namestr
) - 1] = '\0';
1220 printf("%s: %-34.34s[job %d localhost]\n", jobuser
, rankstr
, jobid
);
1221 printf(" %-40.40s%d bytes\n", namestr
, jobsize
);
1224 printf("%-7s %-8.8s%-8d%-32.32s%d bytes\n", rankstr
, jobuser
,
1225 jobid
, jobname
, jobsize
);
1231 ippDelete(response
);
1235 printf("get-jobs failed: %s\n", ippErrorString(cupsLastError()));
1242 cupsLangFree(language
);
1250 * 'smart_gets()' - Get a line of text, removing the trailing CR and/or LF.
1253 char * /* O - Line read or NULL */
1254 smart_gets(char *s
, /* I - Pointer to line buffer */
1255 int len
, /* I - Size of line buffer */
1256 FILE *fp
) /* I - File to read from */
1258 char *ptr
, /* Pointer into line */
1259 *end
; /* End of line */
1260 int ch
; /* Character from file */
1264 * Read the line; unlike fgets(), we read the entire line but dump
1265 * characters that go past the end of the buffer. Also, we accept
1266 * CR, LF, or CR LF for the line endings to be "safe", although
1267 * RFC 1179 specifically says "just use LF".
1273 while ((ch
= getc(fp
)) != EOF
)
1277 else if (ch
== '\r')
1280 * See if a LF follows...
1296 if (ch
== EOF
&& ptr
== s
)
1304 * End of "$Id: cups-lpd.c,v 1.32 2002/04/29 17:25:26 mike Exp $".