]>
Commit | Line | Data |
---|---|---|
85e96ec0 | 1 | /* |
c9d3f842 | 2 | * "$Id$" |
85e96ec0 | 3 | * |
4 | * Line Printer Daemon interface for the Common UNIX Printing System (CUPS). | |
5 | * | |
c9d3f842 | 6 | * Copyright 1997-2005 by Easy Software Products, all rights reserved. |
85e96ec0 | 7 | * |
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 | |
13 | * at: | |
14 | * | |
15 | * Attn: CUPS Licensing Information | |
16 | * Easy Software Products | |
17 | * 44141 Airport View Drive, Suite 204 | |
8650fcf2 | 18 | * Hollywood, Maryland 20636 USA |
85e96ec0 | 19 | * |
edfd3c3d | 20 | * Voice: (301) 373-9600 |
85e96ec0 | 21 | * EMail: cups-info@cups.org |
22 | * WWW: http://www.cups.org | |
23 | * | |
24 | * Contents: | |
25 | * | |
11436d4d | 26 | * main() - Process an incoming LPD request... |
3aba76cc | 27 | * print_file() - Print a file to a printer or class. |
11436d4d | 28 | * recv_print_job() - Receive a print job from the client. |
11436d4d | 29 | * remove_jobs() - Cancel one or more jobs. |
04baa751 | 30 | * send_state() - Send the queue state. |
84b31ee3 | 31 | * smart_gets() - Get a line of text, removing the trailing CR |
32 | * and/or LF. | |
85e96ec0 | 33 | */ |
34 | ||
35 | /* | |
36 | * Include necessary headers... | |
37 | */ | |
38 | ||
39 | #include <cups/cups.h> | |
40 | #include <cups/string.h> | |
41 | #include <cups/language.h> | |
42 | #include <stdlib.h> | |
3aba76cc | 43 | #include <errno.h> |
44 | #include <syslog.h> | |
85e96ec0 | 45 | #include <ctype.h> |
753453e4 | 46 | #include <unistd.h> |
47 | #include <fcntl.h> | |
85e96ec0 | 48 | |
df7507f5 | 49 | #include <sys/types.h> |
50 | #include <sys/socket.h> | |
51 | #include <netinet/in.h> | |
52 | #include <netdb.h> | |
53 | ||
85e96ec0 | 54 | |
55 | /* | |
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: | |
59 | * | |
60 | * printer stream tcp nowait lp /usr/lib/cups/daemon/cups-lpd cups-lpd | |
61 | * | |
62 | * This daemon implements most of RFC 1179 (the unofficial LPD specification) | |
63 | * except for: | |
64 | * | |
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 | |
11436d4d | 67 | * functioning and port-based security is no security at all! |
85e96ec0 | 68 | * |
69 | * - The "Print any waiting jobs" command is a no-op. | |
70 | * | |
84b31ee3 | 71 | * The LPD-to-IPP mapping is as defined in RFC 2569. The report formats |
72 | * currently match the Solaris LPD mini-daemon. | |
85e96ec0 | 73 | */ |
74 | ||
75 | /* | |
76 | * Prototypes... | |
77 | */ | |
78 | ||
be87dcd3 | 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); | |
30d13a1e | 83 | int recv_print_job(const char *dest, int num_defaults, cups_option_t *defaults); |
85e96ec0 | 84 | int remove_jobs(const char *dest, const char *agent, const char *list); |
84b31ee3 | 85 | int send_state(const char *dest, const char *list, int longstatus); |
86 | char *smart_gets(char *s, int len, FILE *fp); | |
85e96ec0 | 87 | |
88 | ||
89 | /* | |
90 | * 'main()' - Process an incoming LPD request... | |
91 | */ | |
92 | ||
4b4a98c0 | 93 | int /* O - Exit status */ |
94 | main(int argc, /* I - Number of command-line arguments */ | |
95 | char *argv[]) /* I - Command-line arguments */ | |
85e96ec0 | 96 | { |
4b4a98c0 | 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 | http_addr_t hostaddr; /* Address of client */ | |
108 | char hostname[256], /* Name of client */ | |
109 | hostip[256], /* IP address */ | |
110 | *hostfamily; /* Address family */ | |
85e96ec0 | 111 | |
112 | ||
113 | /* | |
114 | * Don't buffer the output... | |
115 | */ | |
116 | ||
117 | setbuf(stdout, NULL); | |
118 | ||
3aba76cc | 119 | /* |
120 | * Log things using the "cups-lpd" name... | |
121 | */ | |
122 | ||
123 | openlog("cups-lpd", LOG_PID, LOG_LPR); | |
124 | ||
df7507f5 | 125 | /* |
126 | * Get the address of the client... | |
127 | */ | |
128 | ||
129 | hostlen = sizeof(hostaddr); | |
130 | ||
9b52e5ce | 131 | if (getpeername(0, (struct sockaddr *)&hostaddr, &hostlen)) |
238bae16 | 132 | { |
df7507f5 | 133 | syslog(LOG_WARNING, "Unable to get client address - %s", strerror(errno)); |
238bae16 | 134 | strcpy(hostname, "unknown"); |
135 | } | |
df7507f5 | 136 | else |
137 | { | |
99de6da0 | 138 | httpAddrLookup(&hostaddr, hostname, sizeof(hostname)); |
139 | httpAddrString(&hostaddr, hostip, sizeof(hostip)); | |
df7507f5 | 140 | |
99de6da0 | 141 | #ifdef AF_INET6 |
142 | if (hostaddr.addr.sa_family == AF_INET6) | |
143 | hostfamily = "IPv6"; | |
144 | else | |
145 | #endif /* AF_INET6 */ | |
146 | hostfamily = "IPv4"; | |
147 | ||
148 | syslog(LOG_INFO, "Connection from %s (%s %s)", hostname, hostfamily, hostip); | |
df7507f5 | 149 | } |
150 | ||
30d13a1e | 151 | /* |
152 | * Scan the command-line for options... | |
153 | */ | |
154 | ||
155 | num_defaults = 0; | |
156 | defaults = NULL; | |
157 | ||
238bae16 | 158 | num_defaults = cupsAddOption("job-originating-host-name", hostname, |
159 | num_defaults, &defaults); | |
160 | ||
30d13a1e | 161 | for (i = 1; i < argc; i ++) |
162 | if (argv[i][0] == '-') | |
163 | { | |
33e36762 | 164 | switch (argv[i][1]) |
165 | { | |
166 | case 'o' : /* Option */ | |
167 | if (argv[i][2]) | |
168 | num_defaults = cupsParseOptions(argv[i] + 2, num_defaults, | |
169 | &defaults); | |
170 | else | |
171 | { | |
30d13a1e | 172 | i ++; |
173 | if (i < argc) | |
33e36762 | 174 | num_defaults = cupsParseOptions(argv[i], num_defaults, &defaults); |
30d13a1e | 175 | else |
33e36762 | 176 | syslog(LOG_WARNING, "Expected option string after -o option!"); |
177 | } | |
178 | break; | |
179 | default : | |
7517e57e | 180 | syslog(LOG_WARNING, "Unknown option \"%c\" ignored!", argv[i][1]); |
33e36762 | 181 | break; |
182 | } | |
30d13a1e | 183 | } |
184 | else | |
185 | syslog(LOG_WARNING, "Unknown command-line option \"%s\" ignored!", argv[i]); | |
186 | ||
85e96ec0 | 187 | /* |
188 | * RFC1179 specifies that only 1 daemon command can be received for | |
189 | * every connection. | |
190 | */ | |
191 | ||
84b31ee3 | 192 | if (smart_gets(line, sizeof(line), stdin) == NULL) |
85e96ec0 | 193 | { |
194 | /* | |
195 | * Unable to get command from client! Send an error status and return. | |
196 | */ | |
197 | ||
3aba76cc | 198 | syslog(LOG_ERR, "Unable to get command line from client!"); |
85e96ec0 | 199 | putchar(1); |
200 | return (1); | |
201 | } | |
202 | ||
203 | /* | |
204 | * The first byte is the command byte. After that will be the queue name, | |
205 | * resource list, and/or user name. | |
206 | */ | |
207 | ||
208 | command = line[0]; | |
209 | dest = line + 1; | |
210 | ||
da275f55 | 211 | for (list = dest + 1; *list && !isspace(*list & 255); list ++); |
85e96ec0 | 212 | |
da275f55 | 213 | while (isspace(*list & 255)) |
85e96ec0 | 214 | *list++ = '\0'; |
215 | ||
216 | /* | |
217 | * Do the command... | |
218 | */ | |
219 | ||
220 | switch (command) | |
221 | { | |
222 | default : /* Unknown command */ | |
3aba76cc | 223 | syslog(LOG_ERR, "Unknown LPD command 0x%02X!", command); |
84b31ee3 | 224 | syslog(LOG_ERR, "Command line = %s", line + 1); |
85e96ec0 | 225 | putchar(1); |
226 | ||
227 | status = 1; | |
228 | break; | |
229 | ||
230 | case 0x01 : /* Print any waiting jobs */ | |
df7507f5 | 231 | syslog(LOG_INFO, "Print waiting jobs (no-op)"); |
85e96ec0 | 232 | putchar(0); |
233 | ||
234 | status = 0; | |
235 | break; | |
236 | ||
237 | case 0x02 : /* Receive a printer job */ | |
df7507f5 | 238 | syslog(LOG_INFO, "Receive print job for %s", dest); |
753453e4 | 239 | /* recv_print_job() sends initial status byte */ |
85e96ec0 | 240 | |
30d13a1e | 241 | status = recv_print_job(dest, num_defaults, defaults); |
85e96ec0 | 242 | break; |
243 | ||
244 | case 0x03 : /* Send queue state (short) */ | |
df7507f5 | 245 | syslog(LOG_INFO, "Send queue state (short) for %s %s", dest, list); |
06415a96 | 246 | /* no status byte for this command */ |
85e96ec0 | 247 | |
11436d4d | 248 | status = send_state(dest, list, 0); |
85e96ec0 | 249 | break; |
250 | ||
251 | case 0x04 : /* Send queue state (long) */ | |
df7507f5 | 252 | syslog(LOG_INFO, "Send queue state (long) for %s %s", dest, list); |
06415a96 | 253 | /* no status byte for this command */ |
85e96ec0 | 254 | |
11436d4d | 255 | status = send_state(dest, list, 1); |
85e96ec0 | 256 | break; |
257 | ||
258 | case 0x05 : /* Remove jobs */ | |
85e96ec0 | 259 | /* |
260 | * Grab the agent and skip to the list of users and/or jobs. | |
261 | */ | |
262 | ||
263 | agent = list; | |
264 | ||
da275f55 | 265 | for (; *list && !isspace(*list & 255); list ++); |
266 | while (isspace(*list & 255)) | |
85e96ec0 | 267 | *list++ = '\0'; |
268 | ||
df7507f5 | 269 | syslog(LOG_INFO, "Remove jobs %s on %s by %s", list, dest, agent); |
270 | ||
85e96ec0 | 271 | status = remove_jobs(dest, agent, list); |
753453e4 | 272 | |
273 | putchar(status); | |
85e96ec0 | 274 | break; |
275 | } | |
276 | ||
df7507f5 | 277 | syslog(LOG_INFO, "Closing connection"); |
3aba76cc | 278 | closelog(); |
279 | ||
85e96ec0 | 280 | return (status); |
281 | } | |
282 | ||
283 | ||
eaf10fed | 284 | /* |
285 | * 'check_printer()' - Check that a printer exists and is accepting jobs. | |
286 | */ | |
287 | ||
288 | int /* O - Job ID */ | |
289 | check_printer(const char *name) /* I - Printer or class name */ | |
290 | { | |
291 | http_t *http; /* Connection to server */ | |
292 | ipp_t *request; /* IPP request */ | |
293 | ipp_t *response; /* IPP response */ | |
294 | ipp_attribute_t *attr; /* IPP job-id attribute */ | |
295 | char uri[HTTP_MAX_URI]; /* Printer URI */ | |
296 | cups_lang_t *language; /* Language to use */ | |
297 | int accepting; /* printer-is-accepting-jobs value */ | |
298 | ||
299 | ||
300 | /* | |
301 | * Setup a connection and request data... | |
302 | */ | |
303 | ||
304 | if ((http = httpConnectEncrypt(cupsServer(), ippPort(), | |
305 | cupsEncryption())) == NULL) | |
306 | { | |
307 | syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(), | |
308 | strerror(errno)); | |
309 | return (0); | |
310 | } | |
311 | ||
312 | /* | |
313 | * Build a standard CUPS URI for the printer and fill the standard IPP | |
314 | * attributes... | |
315 | */ | |
316 | ||
317 | if ((request = ippNew()) == NULL) | |
318 | { | |
319 | syslog(LOG_ERR, "Unable to create request: %s", strerror(errno)); | |
320 | httpClose(http); | |
321 | return (0); | |
322 | } | |
323 | ||
324 | request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES; | |
325 | request->request.op.request_id = 1; | |
326 | ||
327 | snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", name); | |
328 | ||
329 | language = cupsLangDefault(); | |
330 | ||
331 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, | |
332 | "attributes-charset", NULL, cupsLangEncoding(language)); | |
333 | ||
334 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, | |
335 | "attributes-natural-language", NULL, | |
336 | language != NULL ? language->language : "C"); | |
337 | ||
338 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
339 | NULL, uri); | |
340 | ||
341 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requested-attributes", | |
342 | NULL, "printer-is-accepting-jobs"); | |
343 | ||
344 | /* | |
345 | * Do the request... | |
346 | */ | |
347 | ||
348 | response = cupsDoRequest(http, request, "/"); | |
349 | ||
350 | if (response == NULL) | |
351 | { | |
352 | syslog(LOG_ERR, "Unable to check printer status - %s", | |
353 | ippErrorString(cupsLastError())); | |
354 | accepting = 0; | |
355 | } | |
356 | else if (response->request.status.status_code > IPP_OK_CONFLICT) | |
357 | { | |
358 | syslog(LOG_ERR, "Unable to check printer status - %s", | |
359 | ippErrorString(response->request.status.status_code)); | |
360 | accepting = 0; | |
361 | } | |
362 | else if ((attr = ippFindAttribute(response, "printer-is-accepting-jobs", | |
363 | IPP_TAG_BOOLEAN)) == NULL) | |
364 | { | |
62623493 | 365 | syslog(LOG_ERR, "No printer-is-accepting-jobs attribute found in response from server!"); |
eaf10fed | 366 | accepting = 0; |
367 | } | |
368 | else | |
369 | accepting = attr->values[0].boolean; | |
370 | ||
371 | if (response != NULL) | |
372 | ippDelete(response); | |
373 | ||
374 | httpClose(http); | |
375 | cupsLangFree(language); | |
376 | ||
377 | return (accepting); | |
378 | } | |
379 | ||
380 | ||
be87dcd3 | 381 | /* |
3aba76cc | 382 | * 'print_file()' - Print a file to a printer or class. |
be87dcd3 | 383 | */ |
384 | ||
385 | int /* O - Job ID */ | |
386 | print_file(const char *name, /* I - Printer or class name */ | |
387 | const char *file, /* I - File to print */ | |
388 | const char *title, /* I - Title of job */ | |
389 | const char *docname, /* I - Name of job file */ | |
753453e4 | 390 | const char *user, /* I - Owner of job */ |
be87dcd3 | 391 | int num_options, /* I - Number of options */ |
392 | cups_option_t *options) /* I - Options */ | |
393 | { | |
be87dcd3 | 394 | http_t *http; /* Connection to server */ |
395 | ipp_t *request; /* IPP request */ | |
396 | ipp_t *response; /* IPP response */ | |
397 | ipp_attribute_t *attr; /* IPP job-id attribute */ | |
398 | char uri[HTTP_MAX_URI]; /* Printer URI */ | |
399 | cups_lang_t *language; /* Language to use */ | |
400 | int jobid; /* New job ID */ | |
401 | ||
402 | ||
403 | /* | |
404 | * Setup a connection and request data... | |
405 | */ | |
406 | ||
b5cb0608 | 407 | if ((http = httpConnectEncrypt(cupsServer(), ippPort(), |
408 | cupsEncryption())) == NULL) | |
3aba76cc | 409 | { |
ceaef43a | 410 | syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(), |
411 | strerror(errno)); | |
be87dcd3 | 412 | return (0); |
3aba76cc | 413 | } |
be87dcd3 | 414 | |
be87dcd3 | 415 | /* |
416 | * Build a standard CUPS URI for the printer and fill the standard IPP | |
417 | * attributes... | |
418 | */ | |
419 | ||
420 | if ((request = ippNew()) == NULL) | |
3aba76cc | 421 | { |
422 | syslog(LOG_ERR, "Unable to create request: %s", strerror(errno)); | |
eaf10fed | 423 | httpClose(http); |
be87dcd3 | 424 | return (0); |
3aba76cc | 425 | } |
be87dcd3 | 426 | |
0a3ac972 | 427 | request->request.op.operation_id = IPP_PRINT_JOB; |
428 | request->request.op.request_id = 1; | |
be87dcd3 | 429 | |
04de52f8 | 430 | snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", name); |
be87dcd3 | 431 | |
eaf10fed | 432 | language = cupsLangDefault(); |
433 | ||
be87dcd3 | 434 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, |
435 | "attributes-charset", NULL, cupsLangEncoding(language)); | |
436 | ||
437 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, | |
438 | "attributes-natural-language", NULL, | |
439 | language != NULL ? language->language : "C"); | |
440 | ||
441 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
442 | NULL, uri); | |
443 | ||
be87dcd3 | 444 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", |
445 | NULL, user); | |
446 | ||
447 | if (title) | |
448 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "job-name", NULL, title); | |
449 | if (docname) | |
450 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "document-name", NULL, docname); | |
451 | ||
452 | /* | |
453 | * Then add all options on the command-line... | |
454 | */ | |
455 | ||
183914a3 | 456 | cupsEncodeOptions(request, num_options, options); |
be87dcd3 | 457 | |
458 | /* | |
459 | * Do the request... | |
460 | */ | |
461 | ||
04de52f8 | 462 | snprintf(uri, sizeof(uri), "/printers/%s", name); |
be87dcd3 | 463 | |
464 | response = cupsDoFileRequest(http, request, uri, file); | |
465 | ||
466 | if (response == NULL) | |
eaf10fed | 467 | { |
468 | syslog(LOG_ERR, "Unable to print file - %s", | |
469 | ippErrorString(cupsLastError())); | |
be87dcd3 | 470 | jobid = 0; |
eaf10fed | 471 | } |
0a3ac972 | 472 | else if (response->request.status.status_code > IPP_OK_CONFLICT) |
eaf10fed | 473 | { |
474 | syslog(LOG_ERR, "Unable to print file - %s", | |
475 | ippErrorString(response->request.status.status_code)); | |
be87dcd3 | 476 | jobid = 0; |
eaf10fed | 477 | } |
be87dcd3 | 478 | else if ((attr = ippFindAttribute(response, "job-id", IPP_TAG_INTEGER)) == NULL) |
eaf10fed | 479 | { |
480 | syslog(LOG_ERR, "No job-id attribute found in response from server!"); | |
be87dcd3 | 481 | jobid = 0; |
eaf10fed | 482 | } |
be87dcd3 | 483 | else |
eaf10fed | 484 | { |
be87dcd3 | 485 | jobid = attr->values[0].integer; |
486 | ||
753453e4 | 487 | syslog(LOG_INFO, "Print file - job ID = %d", jobid); |
eaf10fed | 488 | } |
753453e4 | 489 | |
be87dcd3 | 490 | if (response != NULL) |
491 | ippDelete(response); | |
492 | ||
493 | httpClose(http); | |
494 | cupsLangFree(language); | |
495 | ||
496 | return (jobid); | |
497 | } | |
498 | ||
499 | ||
85e96ec0 | 500 | /* |
501 | * 'recv_print_job()' - Receive a print job from the client. | |
502 | */ | |
503 | ||
504 | int /* O - Command status */ | |
30d13a1e | 505 | recv_print_job(const char *dest, /* I - Destination */ |
4b4a98c0 | 506 | int num_defaults, |
507 | /* I - Number of default options */ | |
30d13a1e | 508 | cups_option_t *defaults) /* I - Default options */ |
85e96ec0 | 509 | { |
510 | int i; /* Looping var */ | |
511 | int status; /* Command status */ | |
1b5bf964 | 512 | int fd; /* Temporary file */ |
513 | FILE *fp; /* File pointer */ | |
84b31ee3 | 514 | char filename[1024]; /* Temporary filename */ |
85e96ec0 | 515 | int bytes; /* Bytes received */ |
84b31ee3 | 516 | char line[256], /* Line from file/stdin */ |
85e96ec0 | 517 | command, /* Command from line */ |
518 | *count, /* Number of bytes */ | |
519 | *name; /* Name of file */ | |
589eb420 | 520 | const char *cupsd_job_sheets; /* Job sheets */ |
85e96ec0 | 521 | int num_data; /* Number of data files */ |
84b31ee3 | 522 | char control[1024], /* Control filename */ |
523 | data[32][256], /* Data files */ | |
524 | temp[32][1024]; /* Temporary files */ | |
85e96ec0 | 525 | char user[1024], /* User name */ |
526 | title[1024], /* Job title */ | |
082b40d2 | 527 | docname[1024], /* Document name */ |
528 | queue[256], /* Printer/class queue */ | |
529 | *instance; /* Printer/class instance */ | |
be87dcd3 | 530 | int num_dests; /* Number of destinations */ |
531 | cups_dest_t *dests, /* Destinations */ | |
532 | *destptr; /* Current destination */ | |
533 | int num_options; /* Number of options */ | |
534 | cups_option_t *options; /* Options */ | |
ef402cda | 535 | int banner; /* Print banner? */ |
85e96ec0 | 536 | |
537 | ||
538 | status = 0; | |
539 | num_data = 0; | |
753453e4 | 540 | fd = -1; |
541 | ||
542 | control[0] = '\0'; | |
082b40d2 | 543 | |
def978d5 | 544 | strlcpy(queue, dest, sizeof(queue)); |
082b40d2 | 545 | |
546 | if ((instance = strrchr(queue, '/')) != NULL) | |
547 | *instance++ = '\0'; | |
85e96ec0 | 548 | |
be87dcd3 | 549 | num_dests = cupsGetDests(&dests); |
082b40d2 | 550 | if ((destptr = cupsGetDest(queue, instance, num_dests, dests)) == NULL) |
be87dcd3 | 551 | { |
1e6bd2df | 552 | /* |
553 | * If the queue name is blank or "lp" then use the default queue. | |
554 | */ | |
3aba76cc | 555 | |
1e6bd2df | 556 | if (!queue[0] || !strcmp(queue, "lp")) |
557 | if ((destptr = cupsGetDest(NULL, NULL, num_dests, dests)) != NULL) | |
558 | strlcpy(queue, destptr->name, sizeof(queue)); | |
753453e4 | 559 | |
1e6bd2df | 560 | if (destptr == NULL) |
561 | { | |
562 | if (instance) | |
563 | syslog(LOG_ERR, "Unknown destination %s/%s!", queue, instance); | |
564 | else | |
565 | syslog(LOG_ERR, "Unknown destination %s!", queue); | |
753453e4 | 566 | |
1e6bd2df | 567 | cupsFreeDests(num_dests, dests); |
568 | ||
569 | putchar(1); | |
570 | ||
571 | return (1); | |
572 | } | |
be87dcd3 | 573 | } |
1e6bd2df | 574 | |
eaf10fed | 575 | if (!check_printer(queue)) |
576 | { | |
577 | cupsFreeDests(num_dests, dests); | |
578 | ||
579 | putchar(1); | |
580 | ||
581 | return (1); | |
582 | } | |
583 | ||
1e6bd2df | 584 | putchar(0); |
be87dcd3 | 585 | |
84b31ee3 | 586 | while (smart_gets(line, sizeof(line), stdin) != NULL) |
85e96ec0 | 587 | { |
588 | if (strlen(line) < 2) | |
589 | { | |
590 | status = 1; | |
591 | break; | |
592 | } | |
593 | ||
594 | command = line[0]; | |
595 | count = line + 1; | |
596 | ||
da275f55 | 597 | for (name = count + 1; *name && !isspace(*name & 255); name ++); |
598 | while (isspace(*name & 255)) | |
85e96ec0 | 599 | *name++ = '\0'; |
600 | ||
601 | switch (command) | |
602 | { | |
603 | default : | |
604 | case 0x01 : /* Abort */ | |
605 | status = 1; | |
606 | break; | |
4b4a98c0 | 607 | |
85e96ec0 | 608 | case 0x02 : /* Receive control file */ |
609 | if (strlen(name) < 2) | |
610 | { | |
3aba76cc | 611 | syslog(LOG_ERR, "Bad control file name \"%s\"", name); |
85e96ec0 | 612 | putchar(1); |
613 | status = 1; | |
614 | break; | |
615 | } | |
616 | ||
753453e4 | 617 | if (control[0]) |
1b5bf964 | 618 | { |
753453e4 | 619 | /* |
620 | * Append to the existing control file - the LPD spec is | |
621 | * not entirely clear, but at least the OS/2 LPD code sends | |
622 | * multiple control files per connection... | |
623 | */ | |
624 | ||
625 | if ((fd = open(control, O_WRONLY)) < 0) | |
626 | { | |
ceaef43a | 627 | syslog(LOG_ERR, |
628 | "Unable to append to temporary control file \"%s\" - %s", | |
629 | control, strerror(errno)); | |
753453e4 | 630 | putchar(1); |
631 | status = 1; | |
632 | break; | |
633 | } | |
1b5bf964 | 634 | |
753453e4 | 635 | lseek(fd, 0, SEEK_END); |
636 | } | |
637 | else | |
638 | { | |
639 | if ((fd = cupsTempFd(control, sizeof(control))) < 0) | |
640 | { | |
ceaef43a | 641 | syslog(LOG_ERR, "Unable to open temporary control file \"%s\" - %s", |
642 | control, strerror(errno)); | |
753453e4 | 643 | putchar(1); |
644 | status = 1; | |
645 | break; | |
646 | } | |
647 | ||
648 | strcpy(filename, control); | |
649 | } | |
85e96ec0 | 650 | break; |
4b4a98c0 | 651 | |
85e96ec0 | 652 | case 0x03 : /* Receive data file */ |
653 | if (strlen(name) < 2) | |
654 | { | |
3aba76cc | 655 | syslog(LOG_ERR, "Bad data file name \"%s\"", name); |
85e96ec0 | 656 | putchar(1); |
657 | status = 1; | |
658 | break; | |
659 | } | |
660 | ||
84b31ee3 | 661 | if (num_data >= (sizeof(data) / sizeof(data[0]))) |
662 | { | |
663 | /* | |
664 | * Too many data files... | |
665 | */ | |
666 | ||
667 | syslog(LOG_ERR, "Too many data files (%d)", num_data); | |
668 | putchar(1); | |
669 | status = 1; | |
670 | break; | |
671 | } | |
672 | ||
def978d5 | 673 | strlcpy(data[num_data], name, sizeof(data[0])); |
85e96ec0 | 674 | |
1b5bf964 | 675 | if ((fd = cupsTempFd(temp[num_data], sizeof(temp[0]))) < 0) |
676 | { | |
ceaef43a | 677 | syslog(LOG_ERR, "Unable to open temporary data file \"%s\" - %s", |
678 | temp[num_data], strerror(errno)); | |
1b5bf964 | 679 | putchar(1); |
680 | status = 1; | |
681 | break; | |
682 | } | |
683 | ||
84b31ee3 | 684 | strcpy(filename, temp[num_data]); |
685 | ||
85e96ec0 | 686 | num_data ++; |
85e96ec0 | 687 | break; |
688 | } | |
689 | ||
690 | putchar(status); | |
691 | ||
692 | if (status) | |
693 | break; | |
694 | ||
85e96ec0 | 695 | /* |
696 | * Copy the data or control file from the client... | |
697 | */ | |
698 | ||
699 | for (i = atoi(count); i > 0; i -= bytes) | |
700 | { | |
701 | if (i > sizeof(line)) | |
702 | bytes = sizeof(line); | |
703 | else | |
704 | bytes = i; | |
705 | ||
706 | if ((bytes = fread(line, 1, bytes, stdin)) > 0) | |
1b5bf964 | 707 | bytes = write(fd, line, bytes); |
85e96ec0 | 708 | |
709 | if (bytes < 1) | |
710 | { | |
edafe5d5 | 711 | syslog(LOG_ERR, "Error while reading file - %s", |
712 | strerror(errno)); | |
85e96ec0 | 713 | status = 1; |
714 | break; | |
715 | } | |
716 | } | |
717 | ||
718 | /* | |
719 | * Read trailing nul... | |
720 | */ | |
721 | ||
722 | if (!status) | |
723 | { | |
edafe5d5 | 724 | if (fread(line, 1, 1, stdin) < 1) |
725 | { | |
726 | status = 1; | |
727 | syslog(LOG_ERR, "Error while reading trailing nul - %s", | |
728 | strerror(errno)); | |
729 | } | |
730 | else if (line[0]) | |
731 | { | |
732 | status = 1; | |
733 | syslog(LOG_ERR, "Trailing character after file is not nul (%02X)!", | |
734 | line[0]); | |
735 | } | |
85e96ec0 | 736 | } |
737 | ||
738 | /* | |
739 | * Close the file and send an acknowledgement... | |
740 | */ | |
741 | ||
baf3bdd2 | 742 | close(fd); |
85e96ec0 | 743 | |
744 | putchar(status); | |
745 | ||
746 | if (status) | |
747 | break; | |
748 | } | |
749 | ||
750 | if (!status) | |
751 | { | |
752 | /* | |
753 | * Process the control file and print stuff... | |
754 | */ | |
755 | ||
84b31ee3 | 756 | if ((fp = fopen(control, "rb")) == NULL) |
85e96ec0 | 757 | status = 1; |
85e96ec0 | 758 | else |
759 | { | |
84b31ee3 | 760 | /* |
761 | * Grab the job information first... | |
762 | */ | |
763 | ||
be87dcd3 | 764 | title[0] = '\0'; |
765 | user[0] = '\0'; | |
766 | docname[0] = '\0'; | |
b68b8820 | 767 | banner = 0; |
85e96ec0 | 768 | |
84b31ee3 | 769 | while (smart_gets(line, sizeof(line), fp) != NULL) |
85e96ec0 | 770 | { |
85e96ec0 | 771 | /* |
772 | * Process control lines... | |
773 | */ | |
774 | ||
775 | switch (line[0]) | |
776 | { | |
777 | case 'J' : /* Job name */ | |
def978d5 | 778 | strlcpy(title, line + 1, sizeof(title)); |
85e96ec0 | 779 | break; |
4b4a98c0 | 780 | |
85e96ec0 | 781 | case 'N' : /* Document name */ |
def978d5 | 782 | strlcpy(docname, line + 1, sizeof(docname)); |
85e96ec0 | 783 | break; |
4b4a98c0 | 784 | |
85e96ec0 | 785 | case 'P' : /* User identification */ |
def978d5 | 786 | strlcpy(user, line + 1, sizeof(user)); |
85e96ec0 | 787 | break; |
4b4a98c0 | 788 | |
ef402cda | 789 | case 'L' : /* Print banner page */ |
790 | banner = 1; | |
791 | break; | |
84b31ee3 | 792 | } |
793 | ||
794 | if (status) | |
795 | break; | |
796 | } | |
797 | ||
798 | /* | |
799 | * Then print the jobs... | |
800 | */ | |
801 | ||
802 | rewind(fp); | |
803 | ||
804 | while (smart_gets(line, sizeof(line), fp) != NULL) | |
805 | { | |
806 | /* | |
807 | * Process control lines... | |
808 | */ | |
809 | ||
810 | switch (line[0]) | |
811 | { | |
85e96ec0 | 812 | case 'c' : /* Plot CIF file */ |
813 | case 'd' : /* Print DVI file */ | |
814 | case 'f' : /* Print formatted file */ | |
815 | case 'g' : /* Plot file */ | |
816 | case 'l' : /* Print file leaving control characters (raw) */ | |
817 | case 'n' : /* Print ditroff output file */ | |
818 | case 'o' : /* Print PostScript output file */ | |
819 | case 'p' : /* Print file with 'pr' format (prettyprint) */ | |
820 | case 'r' : /* File to print with FORTRAN carriage control */ | |
821 | case 't' : /* Print troff output file */ | |
822 | case 'v' : /* Print raster file */ | |
823 | /* | |
753453e4 | 824 | * Check that we have a username... |
85e96ec0 | 825 | */ |
826 | ||
827 | if (!user[0]) | |
828 | { | |
753453e4 | 829 | syslog(LOG_WARNING, "No username specified by client! " |
830 | "Using \"anonymous\"..."); | |
831 | strcpy(user, "anonymous"); | |
85e96ec0 | 832 | } |
833 | ||
be87dcd3 | 834 | /* |
835 | * Copy the default options... | |
836 | */ | |
837 | ||
838 | num_options = 0; | |
839 | options = NULL; | |
30d13a1e | 840 | |
be87dcd3 | 841 | for (i = 0; i < destptr->num_options; i ++) |
842 | num_options = cupsAddOption(destptr->options[i].name, | |
843 | destptr->options[i].value, | |
844 | num_options, &options); | |
b68b8820 | 845 | for (i = 0; i < num_defaults; i ++) |
846 | num_options = cupsAddOption(defaults[i].name, | |
847 | defaults[i].value, | |
848 | num_options, &options); | |
30d13a1e | 849 | |
be87dcd3 | 850 | /* |
b68b8820 | 851 | * If a banner was requested and it's not overridden by a |
852 | * command line option and the destination's default is none | |
853 | * then add the standard banner... | |
be87dcd3 | 854 | */ |
855 | ||
b68b8820 | 856 | if (banner && |
857 | cupsGetOption("job-sheets", num_defaults, defaults) == NULL && | |
589eb420 | 858 | ((cupsd_job_sheets = cupsGetOption("job-sheets", |
b68b8820 | 859 | destptr->num_options, |
860 | destptr->options)) == NULL || | |
589eb420 | 861 | !strcmp(cupsd_job_sheets, "none,none"))) |
b68b8820 | 862 | { |
863 | num_options = cupsAddOption("job-sheets", "standard", | |
ef402cda | 864 | num_options, &options); |
b68b8820 | 865 | } |
866 | ||
867 | /* | |
868 | * Add additional options as needed... | |
869 | */ | |
ef402cda | 870 | |
be87dcd3 | 871 | if (line[0] == 'l') |
872 | num_options = cupsAddOption("raw", "", num_options, &options); | |
873 | ||
874 | if (line[0] == 'p') | |
875 | num_options = cupsAddOption("prettyprint", "", num_options, | |
876 | &options); | |
877 | ||
85e96ec0 | 878 | /* |
879 | * Figure out which file we are printing... | |
880 | */ | |
881 | ||
882 | for (i = 0; i < num_data; i ++) | |
883 | if (strcmp(data[i], line + 1) == 0) | |
884 | break; | |
885 | ||
886 | if (i >= num_data) | |
887 | { | |
888 | status = 1; | |
889 | break; | |
890 | } | |
891 | ||
892 | /* | |
be87dcd3 | 893 | * Send the print request... |
85e96ec0 | 894 | */ |
895 | ||
84b31ee3 | 896 | if (print_file(queue, temp[i], title, docname, user, num_options, |
be87dcd3 | 897 | options) == 0) |
898 | status = 1; | |
899 | else | |
900 | status = 0; | |
84b31ee3 | 901 | |
902 | cupsFreeOptions(num_options, options); | |
85e96ec0 | 903 | break; |
904 | } | |
905 | ||
906 | if (status) | |
907 | break; | |
908 | } | |
909 | ||
910 | fclose(fp); | |
85e96ec0 | 911 | } |
912 | } | |
913 | ||
914 | /* | |
915 | * Clean up all temporary files and return... | |
916 | */ | |
917 | ||
84b31ee3 | 918 | unlink(control); |
919 | ||
920 | for (i = 0; i < num_data; i ++) | |
921 | unlink(temp[i]); | |
85e96ec0 | 922 | |
be87dcd3 | 923 | cupsFreeDests(num_dests, dests); |
924 | ||
85e96ec0 | 925 | return (status); |
926 | } | |
927 | ||
928 | ||
84b31ee3 | 929 | /* |
930 | * 'remove_jobs()' - Cancel one or more jobs. | |
931 | */ | |
932 | ||
933 | int /* O - Command status */ | |
934 | remove_jobs(const char *dest, /* I - Destination */ | |
935 | const char *agent, /* I - User agent */ | |
936 | const char *list) /* I - List of jobs or users */ | |
937 | { | |
938 | int id; /* Job ID */ | |
939 | http_t *http; /* HTTP server connection */ | |
940 | ipp_t *request, /* IPP Request */ | |
941 | *response; /* IPP Response */ | |
942 | cups_lang_t *language; /* Default language */ | |
943 | char uri[HTTP_MAX_URI]; /* Job URI */ | |
944 | ||
945 | ||
946 | (void)dest; /* Suppress compiler warnings... */ | |
947 | ||
948 | /* | |
949 | * Try connecting to the local server... | |
950 | */ | |
951 | ||
b5cb0608 | 952 | if ((http = httpConnectEncrypt(cupsServer(), ippPort(), |
953 | cupsEncryption())) == NULL) | |
753453e4 | 954 | { |
ceaef43a | 955 | syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(), |
956 | strerror(errno)); | |
84b31ee3 | 957 | return (1); |
753453e4 | 958 | } |
84b31ee3 | 959 | |
960 | language = cupsLangDefault(); | |
961 | ||
962 | /* | |
963 | * Loop for each job... | |
964 | */ | |
965 | ||
966 | while ((id = atoi(list)) > 0) | |
967 | { | |
968 | /* | |
969 | * Skip job ID in list... | |
970 | */ | |
971 | ||
da275f55 | 972 | while (isdigit(*list & 255)) |
84b31ee3 | 973 | list ++; |
da275f55 | 974 | while (isspace(*list & 255)) |
84b31ee3 | 975 | list ++; |
976 | ||
977 | /* | |
978 | * Build an IPP_CANCEL_JOB request, which requires the following | |
979 | * attributes: | |
980 | * | |
981 | * attributes-charset | |
982 | * attributes-natural-language | |
983 | * job-uri | |
984 | * requesting-user-name | |
985 | */ | |
986 | ||
987 | request = ippNew(); | |
988 | ||
0a3ac972 | 989 | request->request.op.operation_id = IPP_CANCEL_JOB; |
990 | request->request.op.request_id = 1; | |
84b31ee3 | 991 | |
992 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, | |
993 | "attributes-charset", NULL, cupsLangEncoding(language)); | |
994 | ||
995 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, | |
996 | "attributes-natural-language", NULL, language->language); | |
997 | ||
998 | sprintf(uri, "ipp://localhost/jobs/%d", id); | |
999 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, uri); | |
1000 | ||
1001 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, | |
1002 | "requesting-user-name", NULL, agent); | |
1003 | ||
1004 | /* | |
1005 | * Do the request and get back a response... | |
1006 | */ | |
1007 | ||
1008 | if ((response = cupsDoRequest(http, request, "/jobs")) != NULL) | |
1009 | { | |
0a3ac972 | 1010 | if (response->request.status.status_code > IPP_OK_CONFLICT) |
84b31ee3 | 1011 | { |
df7507f5 | 1012 | syslog(LOG_WARNING, "Cancel of job ID %d failed: %s\n", id, |
0a3ac972 | 1013 | ippErrorString(response->request.status.status_code)); |
84b31ee3 | 1014 | ippDelete(response); |
1015 | cupsLangFree(language); | |
1016 | httpClose(http); | |
1017 | return (1); | |
1018 | } | |
df7507f5 | 1019 | else |
1020 | syslog(LOG_INFO, "Job ID %d cancelled", id); | |
84b31ee3 | 1021 | |
1022 | ippDelete(response); | |
1023 | } | |
1024 | else | |
1025 | { | |
df7507f5 | 1026 | syslog(LOG_WARNING, "Cancel of job ID %d failed: %s\n", id, |
1027 | ippErrorString(cupsLastError())); | |
84b31ee3 | 1028 | cupsLangFree(language); |
1029 | httpClose(http); | |
1030 | return (1); | |
1031 | } | |
1032 | } | |
1033 | ||
1034 | cupsLangFree(language); | |
1035 | httpClose(http); | |
1036 | ||
1037 | return (0); | |
1038 | } | |
1039 | ||
1040 | ||
85e96ec0 | 1041 | /* |
753453e4 | 1042 | * 'send_state()' - Send the queue state. |
85e96ec0 | 1043 | */ |
1044 | ||
1045 | int /* O - Command status */ | |
11436d4d | 1046 | send_state(const char *dest, /* I - Destination */ |
1047 | const char *list, /* I - Job or user */ | |
1048 | int longstatus) /* I - List of jobs or users */ | |
85e96ec0 | 1049 | { |
11436d4d | 1050 | int id; /* Job ID from list */ |
1051 | http_t *http; /* HTTP server connection */ | |
1052 | ipp_t *request, /* IPP Request */ | |
1053 | *response; /* IPP Response */ | |
1054 | ipp_attribute_t *attr; /* Current attribute */ | |
1055 | cups_lang_t *language; /* Default language */ | |
1056 | ipp_pstate_t state; /* Printer state */ | |
1057 | const char *jobdest, /* Pointer into job-printer-uri */ | |
1058 | *jobuser, /* Pointer to job-originating-user-name */ | |
1059 | *jobname; /* Pointer to job-name */ | |
1060 | ipp_jstate_t jobstate; /* job-state */ | |
1061 | int jobid, /* job-id */ | |
1062 | jobsize, /* job-k-octets */ | |
1063 | jobcount, /* Number of jobs */ | |
1064 | jobcopies, /* Number of copies */ | |
1065 | rank; /* Rank of job */ | |
1066 | char rankstr[255]; /* Rank string */ | |
1067 | char namestr[1024]; /* Job name string */ | |
1068 | char uri[HTTP_MAX_URI]; /* Printer URI */ | |
f56f8d5c | 1069 | char queue[256], /* Printer/class queue */ |
1070 | *instance; /* Printer/class instance */ | |
6db7190f | 1071 | static const char * const ranks[10] = /* Ranking strings */ |
11436d4d | 1072 | { |
1073 | "th", | |
1074 | "st", | |
1075 | "nd", | |
1076 | "rd", | |
1077 | "th", | |
1078 | "th", | |
1079 | "th", | |
1080 | "th", | |
1081 | "th", | |
1082 | "th" | |
1083 | }; | |
6db7190f | 1084 | static const char * const requested[] = |
1085 | { /* Requested attributes */ | |
92db514a | 1086 | "job-id", |
1087 | "job-k-octets", | |
1088 | "job-state", | |
1089 | "job-printer-uri", | |
1090 | "job-originating-user-name", | |
1091 | "job-name", | |
1092 | "copies" | |
1093 | }; | |
85e96ec0 | 1094 | |
1095 | ||
f56f8d5c | 1096 | /* |
1097 | * Remove instance from destination, if any... | |
1098 | */ | |
1099 | ||
def978d5 | 1100 | strlcpy(queue, dest, sizeof(queue)); |
f56f8d5c | 1101 | |
1102 | if ((instance = strrchr(queue, '/')) != NULL) | |
1103 | *instance++ = '\0'; | |
1104 | ||
11436d4d | 1105 | /* |
1106 | * Try connecting to the local server... | |
1107 | */ | |
85e96ec0 | 1108 | |
b5cb0608 | 1109 | if ((http = httpConnectEncrypt(cupsServer(), ippPort(), |
1110 | cupsEncryption())) == NULL) | |
753453e4 | 1111 | { |
ceaef43a | 1112 | syslog(LOG_ERR, "Unable to connect to server %s: %s", cupsServer(), |
1113 | strerror(errno)); | |
1114 | printf("Unable to connect to server %s: %s", cupsServer(), strerror(errno)); | |
11436d4d | 1115 | return (1); |
753453e4 | 1116 | } |
11436d4d | 1117 | |
1118 | /* | |
1119 | * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following | |
1120 | * attributes: | |
1121 | * | |
1122 | * attributes-charset | |
1123 | * attributes-natural-language | |
1124 | * printer-uri | |
1125 | */ | |
1126 | ||
1127 | request = ippNew(); | |
1128 | ||
0a3ac972 | 1129 | request->request.op.operation_id = IPP_GET_PRINTER_ATTRIBUTES; |
1130 | request->request.op.request_id = 1; | |
11436d4d | 1131 | |
1132 | language = cupsLangDefault(); | |
1133 | ||
1134 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, | |
1135 | "attributes-charset", NULL, cupsLangEncoding(language)); | |
1136 | ||
1137 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, | |
1138 | "attributes-natural-language", NULL, language->language); | |
1139 | ||
a6988fb1 | 1140 | snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", queue); |
11436d4d | 1141 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, |
1142 | "printer-uri", NULL, uri); | |
1143 | ||
92db514a | 1144 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, |
1145 | "requested-attributes", NULL, "printer-state"); | |
1146 | ||
11436d4d | 1147 | /* |
1148 | * Do the request and get back a response... | |
1149 | */ | |
1150 | ||
1151 | if ((response = cupsDoRequest(http, request, "/")) != NULL) | |
1152 | { | |
0a3ac972 | 1153 | if (response->request.status.status_code > IPP_OK_CONFLICT) |
11436d4d | 1154 | { |
df7507f5 | 1155 | syslog(LOG_WARNING, "Unable to get printer list: %s\n", |
0a3ac972 | 1156 | ippErrorString(response->request.status.status_code)); |
06415a96 | 1157 | printf("Unable to get printer list: %s\n", |
1158 | ippErrorString(response->request.status.status_code)); | |
11436d4d | 1159 | ippDelete(response); |
1160 | return (1); | |
1161 | } | |
1162 | ||
1163 | if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL) | |
1164 | state = (ipp_pstate_t)attr->values[0].integer; | |
1165 | else | |
1166 | state = IPP_PRINTER_STOPPED; | |
1167 | ||
1168 | switch (state) | |
1169 | { | |
1170 | case IPP_PRINTER_IDLE : | |
1171 | printf("%s is ready\n", dest); | |
1172 | break; | |
1173 | case IPP_PRINTER_PROCESSING : | |
1174 | printf("%s is ready and printing\n", dest); | |
1175 | break; | |
1176 | case IPP_PRINTER_STOPPED : | |
1177 | printf("%s is not ready\n", dest); | |
1178 | break; | |
1179 | } | |
1180 | ||
1181 | ippDelete(response); | |
1182 | } | |
1183 | else | |
1184 | { | |
df7507f5 | 1185 | syslog(LOG_WARNING, "Unable to get printer list: %s\n", |
1186 | ippErrorString(cupsLastError())); | |
06415a96 | 1187 | printf("Unable to get printer list: %s\n", |
1188 | ippErrorString(cupsLastError())); | |
11436d4d | 1189 | return (1); |
1190 | } | |
1191 | ||
1192 | /* | |
1193 | * Build an IPP_GET_JOBS or IPP_GET_JOB_ATTRIBUTES request, which requires | |
1194 | * the following attributes: | |
1195 | * | |
1196 | * attributes-charset | |
1197 | * attributes-natural-language | |
1198 | * job-uri or printer-uri | |
1199 | */ | |
1200 | ||
1201 | id = atoi(list); | |
1202 | ||
1203 | request = ippNew(); | |
1204 | ||
0a3ac972 | 1205 | request->request.op.operation_id = id ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS; |
1206 | request->request.op.request_id = 1; | |
11436d4d | 1207 | |
92db514a | 1208 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, |
1209 | "attributes-charset", NULL, cupsLangEncoding(language)); | |
11436d4d | 1210 | |
92db514a | 1211 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, |
1212 | "attributes-natural-language", NULL, language->language); | |
11436d4d | 1213 | |
04de52f8 | 1214 | snprintf(uri, sizeof(uri), "ipp://localhost/printers/%s", queue); |
11436d4d | 1215 | |
1216 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", | |
1217 | NULL, uri); | |
1218 | ||
1219 | if (id) | |
1220 | ippAddInteger(request, IPP_TAG_OPERATION, IPP_TAG_INTEGER, "job-id", id); | |
1221 | else | |
1222 | { | |
1223 | ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, | |
1224 | "requesting-user-name", NULL, list); | |
1225 | ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1); | |
1226 | } | |
1227 | ||
92db514a | 1228 | ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, |
1229 | "requested-attributes", sizeof(requested) / sizeof(requested[0]), | |
1230 | NULL, requested); | |
1231 | ||
11436d4d | 1232 | /* |
1233 | * Do the request and get back a response... | |
1234 | */ | |
1235 | ||
1236 | jobcount = 0; | |
1237 | ||
1238 | if ((response = cupsDoRequest(http, request, "/")) != NULL) | |
1239 | { | |
0a3ac972 | 1240 | if (response->request.status.status_code > IPP_OK_CONFLICT) |
11436d4d | 1241 | { |
1242 | printf("get-jobs failed: %s\n", | |
0a3ac972 | 1243 | ippErrorString(response->request.status.status_code)); |
11436d4d | 1244 | ippDelete(response); |
1245 | return (1); | |
1246 | } | |
1247 | ||
1248 | rank = 1; | |
1249 | ||
1250 | /* | |
1251 | * Loop through the job list and display them... | |
1252 | */ | |
1253 | ||
1254 | for (attr = response->attrs; attr != NULL; attr = attr->next) | |
1255 | { | |
1256 | /* | |
1257 | * Skip leading attributes until we hit a job... | |
1258 | */ | |
1259 | ||
92db514a | 1260 | while (attr != NULL && |
1261 | (attr->group_tag != IPP_TAG_JOB || attr->name == NULL)) | |
11436d4d | 1262 | attr = attr->next; |
1263 | ||
1264 | if (attr == NULL) | |
1265 | break; | |
1266 | ||
1267 | /* | |
1268 | * Pull the needed attributes from this job... | |
1269 | */ | |
1270 | ||
1271 | jobid = 0; | |
1272 | jobsize = 0; | |
1273 | jobstate = IPP_JOB_PENDING; | |
1274 | jobname = "untitled"; | |
1275 | jobuser = NULL; | |
1276 | jobdest = NULL; | |
1277 | jobcopies = 1; | |
1278 | ||
1279 | while (attr != NULL && attr->group_tag == IPP_TAG_JOB) | |
1280 | { | |
1281 | if (strcmp(attr->name, "job-id") == 0 && | |
1282 | attr->value_tag == IPP_TAG_INTEGER) | |
1283 | jobid = attr->values[0].integer; | |
1284 | ||
1285 | if (strcmp(attr->name, "job-k-octets") == 0 && | |
1286 | attr->value_tag == IPP_TAG_INTEGER) | |
1287 | jobsize = attr->values[0].integer * 1024; | |
1288 | ||
1289 | if (strcmp(attr->name, "job-state") == 0 && | |
1290 | attr->value_tag == IPP_TAG_ENUM) | |
1291 | jobstate = (ipp_jstate_t)attr->values[0].integer; | |
1292 | ||
1293 | if (strcmp(attr->name, "job-printer-uri") == 0 && | |
1294 | attr->value_tag == IPP_TAG_URI) | |
1295 | if ((jobdest = strrchr(attr->values[0].string.text, '/')) != NULL) | |
1296 | jobdest ++; | |
1297 | ||
1298 | if (strcmp(attr->name, "job-originating-user-name") == 0 && | |
1299 | attr->value_tag == IPP_TAG_NAME) | |
1300 | jobuser = attr->values[0].string.text; | |
1301 | ||
1302 | if (strcmp(attr->name, "job-name") == 0 && | |
1303 | attr->value_tag == IPP_TAG_NAME) | |
1304 | jobname = attr->values[0].string.text; | |
1305 | ||
1306 | if (strcmp(attr->name, "copies") == 0 && | |
1307 | attr->value_tag == IPP_TAG_INTEGER) | |
1308 | jobcopies = attr->values[0].integer; | |
1309 | ||
1310 | attr = attr->next; | |
1311 | } | |
1312 | ||
1313 | /* | |
1314 | * See if we have everything needed... | |
1315 | */ | |
1316 | ||
1317 | if (jobdest == NULL || jobid == 0) | |
1318 | { | |
1319 | if (attr == NULL) | |
1320 | break; | |
1321 | else | |
1322 | continue; | |
1323 | } | |
1324 | ||
1325 | if (!longstatus && jobcount == 0) | |
92db514a | 1326 | puts("Rank Owner Job File(s) Total Size"); |
11436d4d | 1327 | |
1328 | jobcount ++; | |
1329 | ||
1330 | /* | |
1331 | * Display the job... | |
1332 | */ | |
1333 | ||
1334 | if (jobstate == IPP_JOB_PROCESSING) | |
1335 | strcpy(rankstr, "active"); | |
1336 | else | |
1337 | { | |
a6988fb1 | 1338 | snprintf(rankstr, sizeof(rankstr), "%d%s", rank, ranks[rank % 10]); |
11436d4d | 1339 | rank ++; |
1340 | } | |
1341 | ||
1342 | if (longstatus) | |
1343 | { | |
1344 | puts(""); | |
1345 | ||
1346 | if (jobcopies > 1) | |
04de52f8 | 1347 | snprintf(namestr, sizeof(namestr), "%d copies of %s", jobcopies, |
11436d4d | 1348 | jobname); |
1349 | else | |
def978d5 | 1350 | strlcpy(namestr, jobname, sizeof(namestr)); |
11436d4d | 1351 | |
f9e6375d | 1352 | printf("%s: %-33.33s [job %d localhost]\n", jobuser, rankstr, jobid); |
1353 | printf(" %-39.39s %d bytes\n", namestr, jobsize); | |
11436d4d | 1354 | } |
1355 | else | |
f9e6375d | 1356 | printf("%-7s %-7.7s %-7d %-31.31s %d bytes\n", rankstr, jobuser, |
11436d4d | 1357 | jobid, jobname, jobsize); |
1358 | ||
1359 | if (attr == NULL) | |
1360 | break; | |
1361 | } | |
1362 | ||
1363 | ippDelete(response); | |
1364 | } | |
1365 | else | |
1366 | { | |
1367 | printf("get-jobs failed: %s\n", ippErrorString(cupsLastError())); | |
1368 | return (1); | |
1369 | } | |
1370 | ||
1371 | if (jobcount == 0) | |
1372 | puts("no entries"); | |
1373 | ||
1374 | cupsLangFree(language); | |
1375 | httpClose(http); | |
1376 | ||
1377 | return (0); | |
85e96ec0 | 1378 | } |
1379 | ||
1380 | ||
1381 | /* | |
84b31ee3 | 1382 | * 'smart_gets()' - Get a line of text, removing the trailing CR and/or LF. |
85e96ec0 | 1383 | */ |
1384 | ||
4b4a98c0 | 1385 | char * /* O - Line read or NULL */ |
1386 | smart_gets(char *s, /* I - Pointer to line buffer */ | |
1387 | int len, /* I - Size of line buffer */ | |
1388 | FILE *fp) /* I - File to read from */ | |
85e96ec0 | 1389 | { |
4b4a98c0 | 1390 | char *ptr, /* Pointer into line */ |
1391 | *end; /* End of line */ | |
1392 | int ch; /* Character from file */ | |
11436d4d | 1393 | |
30d13a1e | 1394 | |
11436d4d | 1395 | /* |
84b31ee3 | 1396 | * Read the line; unlike fgets(), we read the entire line but dump |
1397 | * characters that go past the end of the buffer. Also, we accept | |
1398 | * CR, LF, or CR LF for the line endings to be "safe", although | |
1399 | * RFC 1179 specifically says "just use LF". | |
11436d4d | 1400 | */ |
1401 | ||
84b31ee3 | 1402 | ptr = s; |
1403 | end = s + len - 1; | |
11436d4d | 1404 | |
84b31ee3 | 1405 | while ((ch = getc(fp)) != EOF) |
11436d4d | 1406 | { |
84b31ee3 | 1407 | if (ch == '\n') |
1408 | break; | |
1409 | else if (ch == '\r') | |
1410 | { | |
1411 | /* | |
1412 | * See if a LF follows... | |
1413 | */ | |
11436d4d | 1414 | |
84b31ee3 | 1415 | ch = getc(fp); |
11436d4d | 1416 | |
84b31ee3 | 1417 | if (ch != '\n') |
1418 | ungetc(ch, fp); | |
11436d4d | 1419 | |
84b31ee3 | 1420 | break; |
11436d4d | 1421 | } |
84b31ee3 | 1422 | else if (ptr < end) |
1423 | *ptr++ = ch; | |
11436d4d | 1424 | } |
1425 | ||
84b31ee3 | 1426 | *ptr = '\0'; |
11436d4d | 1427 | |
84b31ee3 | 1428 | if (ch == EOF && ptr == s) |
1429 | return (NULL); | |
1430 | else | |
1431 | return (s); | |
85e96ec0 | 1432 | } |
1433 | ||
1434 | ||
1435 | /* | |
c9d3f842 | 1436 | * End of "$Id$". |
85e96ec0 | 1437 | */ |